2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.python;
18 import com.intellij.psi.PsiElement;
19 import com.intellij.psi.search.PsiElementProcessor;
20 import com.intellij.psi.util.PsiTreeUtil;
21 import com.jetbrains.python.documentation.docstrings.*;
22 import com.jetbrains.python.documentation.docstrings.SectionBasedDocString.Section;
23 import com.jetbrains.python.documentation.docstrings.SectionBasedDocString.SectionField;
24 import com.jetbrains.python.fixtures.PyTestCase;
25 import com.jetbrains.python.psi.PyStringLiteralExpression;
26 import com.jetbrains.python.toolbox.Substring;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
30 import java.util.List;
33 * @author Mikhail Golubev
35 public class PySectionBasedDocStringTest extends PyTestCase {
37 public void testSimpleGoogleDocString() {
38 checkSimpleDocstringStructure(findAndParseGoogleStyleDocString());
41 public void testSimpleNumpyDocstring() {
42 checkSimpleDocstringStructure(findAndParseNumpyStyleDocString());
45 private static void checkSimpleDocstringStructure(@NotNull SectionBasedDocString docString) {
46 assertEquals("Summary", docString.getSummary());
47 final List<Section> sections = docString.getSections();
48 assertSize(3, sections);
50 assertEquals("parameters", sections.get(0).getNormalizedTitle());
51 final List<SectionField> paramFields = sections.get(0).getFields();
52 assertSize(2, paramFields);
53 final SectionField param1 = paramFields.get(0);
54 assertEquals("x", param1.getName());
55 assertEquals("int", param1.getType());
56 assertEquals("first parameter", param1.getDescription());
58 final SectionField param2 = paramFields.get(1);
59 assertEquals("y", param2.getName());
60 assertEmpty(param2.getType());
61 assertEquals("second parameter\n" +
62 "with longer description", param2.getDescription());
64 assertEquals("raises", sections.get(1).getNormalizedTitle());
65 final List<SectionField> exceptionFields = sections.get(1).getFields();
66 assertSize(1, exceptionFields);
67 final SectionField exception1 = exceptionFields.get(0);
68 assertEmpty(exception1.getName());
69 assertEquals("Exception", exception1.getType());
70 assertEquals("if anything bad happens", exception1.getDescription());
72 assertEquals("returns", sections.get(2).getNormalizedTitle());
73 final List<SectionField> returnFields = sections.get(2).getFields();
74 assertSize(1, returnFields);
75 final SectionField return1 = returnFields.get(0);
76 assertEmpty(return1.getName());
77 assertEquals("None", return1.getType());
78 assertEquals("always", return1.getDescription());
82 private Substring findAndParseDocString() {
83 myFixture.configureByFile(getTestName(true) + ".py");
84 final String docStringText = findFirstDocString();
86 assertNotNull(docStringText);
87 return new Substring(docStringText);
91 private GoogleCodeStyleDocString findAndParseGoogleStyleDocString() {
92 return new GoogleCodeStyleDocString(findAndParseDocString());
96 private NumpyDocString findAndParseNumpyStyleDocString() {
97 return new NumpyDocString(findAndParseDocString());
101 private String findFirstDocString() {
102 final PsiElementProcessor.FindElement<PsiElement> processor = new PsiElementProcessor.FindElement<PsiElement>() {
104 public boolean execute(@NotNull PsiElement element) {
105 if (element instanceof PyStringLiteralExpression && element.getFirstChild().getNode().getElementType() == PyTokenTypes.DOCSTRING) {
106 return setFound(element);
111 PsiTreeUtil.processElements(myFixture.getFile(), processor);
112 if (!processor.isFound()) {
115 final PsiElement foundElement = processor.getFoundElement();
116 assertNotNull(foundElement);
117 return ((PyStringLiteralExpression)foundElement).getStringValue();
120 public void testSectionStartAfterQuotes() {
121 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
122 assertEmpty(docString.getSummary());
124 assertSize(2, docString.getSections());
126 final Section examplesSection = docString.getSections().get(0);
127 assertEquals("examples", examplesSection.getNormalizedTitle());
128 assertSize(1, examplesSection.getFields());
129 final SectionField example1 = examplesSection.getFields().get(0);
130 assertEmpty(example1.getName());
131 assertEmpty(example1.getType());
132 assertEquals("Useless call\n" +
133 "func() == func()", example1.getDescription());
135 final Section notesSection = docString.getSections().get(1);
136 assertEquals("notes", notesSection.getNormalizedTitle());
137 assertSize(1, notesSection.getFields());
138 final SectionField note1 = notesSection.getFields().get(0);
139 assertEmpty(note1.getName());
140 assertEmpty(note1.getType());
141 assertEquals("some\n" +
142 "notes", note1.getDescription());
145 public void testTypeReferences() {
146 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
147 assertEmpty(docString.getSummary());
148 assertSize(2, docString.getSections());
150 final Section paramSection = docString.getSections().get(0);
151 assertSize(1, paramSection.getFields());
152 final SectionField param1 = paramSection.getFields().get(0);
153 assertEquals("a1", param1.getName());
154 assertEquals(":class:`MyClass`", param1.getType());
155 assertEquals("used to call :def:`my_function` and access :attr:`my_attr`", param1.getDescription());
157 final Section raisesSection = docString.getSections().get(1);
158 assertSize(1, raisesSection.getFields());
159 final SectionField exception1 = raisesSection.getFields().get(0);
160 assertEmpty(exception1.getName());
161 assertEquals(":class:`MyException`", exception1.getType());
162 assertEquals("thrown in case of any error", exception1.getDescription());
165 public void testNestedIndentation() {
166 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
167 assertSize(1, docString.getSections());
168 final Section section1 = docString.getSections().get(0);
169 assertEquals("parameters", section1.getNormalizedTitle());
170 assertSize(1, section1.getFields());
171 final SectionField param1 = section1.getFields().get(0);
172 assertEquals("x", param1.getName());
173 assertEquals("int", param1.getType());
174 assertEquals("first line of the description\n" +
180 " assert func(42) is None", param1.getDescription());
183 public void testMultilineSummary() {
184 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
185 assertEquals("First line\n" +
187 "Third line", docString.getSummary());
190 public void testNamedReturnsAndYields() {
191 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
192 assertEmpty(docString.getSummary());
193 assertSize(2, docString.getSections());
195 final Section returnSection = docString.getSections().get(0);
196 assertSize(2, returnSection.getFields());
198 final SectionField return1 = returnSection.getFields().get(0);
199 assertEquals("status_code", return1.getName());
200 assertEquals("int", return1.getType());
201 assertEquals("HTTP status code", return1.getDescription());
203 final SectionField return2 = returnSection.getFields().get(1);
204 assertEquals("template", return2.getName());
205 assertEquals("str", return2.getType());
206 assertEquals("path to template in template roots", return2.getDescription());
208 final Section yieldSection = docString.getSections().get(1);
209 assertSize(1, yieldSection.getFields());
210 final SectionField yield1 = yieldSection.getFields().get(0);
211 assertEquals("progress", yield1.getName());
212 assertEquals("float", yield1.getType());
213 assertEquals("floating point value in range [0, 1) indicating progress\n" +
214 "of the task", yield1.getDescription());
217 public void testNumpySignature() {
218 final NumpyDocString docString = findAndParseNumpyStyleDocString();
219 assertEquals("a.diagonal(offset=0, axis1=0, axis2=1)", docString.getSignature());
220 assertEquals("Return specified diagonals.", docString.getSummary());
223 public void testNumpySectionBlockBreaksOnDoubleEmptyLine() {
224 final NumpyDocString docString = findAndParseNumpyStyleDocString();
225 assertSize(1, docString.getSections());
226 final Section paramSection = docString.getSections().get(0);
227 assertEquals("parameters", paramSection.getNormalizedTitle());
228 assertSize(1, paramSection.getFields());
229 final SectionField param1 = paramSection.getFields().get(0);
230 assertEquals("x", param1.getName());
231 assertEmpty(param1.getType());
232 assertEquals("First line\n" +
235 "Line after single break", param1.getDescription());
238 public void testGoogleEmptyParamTypeInParenthesis() {
239 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
240 assertSize(1, docString.getSections());
241 final Section paramSection = docString.getSections().get(0);
242 assertEquals("parameters", paramSection.getNormalizedTitle());
243 assertSize(1, paramSection.getFields());
244 final SectionField param1 = paramSection.getFields().get(0);
245 assertEquals("x", param1.getName());
246 assertEmpty(param1.getDescription());
247 assertEmpty(param1.getType());
248 assertNotNull(param1.getTypeAsSubstring());
249 assertEquals(26, param1.getTypeAsSubstring().getStartOffset());
250 assertEquals(26, param1.getTypeAsSubstring().getEndOffset());
253 public void testGoogleReturnTypeNoDescription() {
254 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
255 assertSize(1, docString.getSections());
256 final Section returnSection = docString.getSections().get(0);
257 assertEquals("returns", returnSection.getNormalizedTitle());
258 assertSize(1, returnSection.getFields());
259 final SectionField return1 = returnSection.getFields().get(0);
260 assertEmpty(return1.getName());
261 assertEmpty(return1.getDescription());
262 assertEquals("object", return1.getType());
263 assertNotNull(return1.getTypeAsSubstring());
264 assertEquals(20, return1.getTypeAsSubstring().getStartOffset());
265 assertEquals(26, return1.getTypeAsSubstring().getEndOffset());
268 public void testGoogleNoEmptyLineAfterSummary() {
269 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
270 assertEquals("Summary.", docString.getSummary());
271 assertSize(1, docString.getSections());
272 assertSize(1, docString.getSections().get(0).getFields());
275 public void testGoogleParametersSectionWithoutSummary() {
276 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
277 assertEmpty(docString.getSummary());
278 assertSize(1, docString.getSections());
279 final Section paramSection = docString.getSections().get(0);
280 assertEquals("parameters", paramSection.getNormalizedTitle());
281 assertSize(1, paramSection.getFields());
284 public void testGoogleKeywordArgumentsSection() {
285 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
286 assertEmpty(docString.getSummary());
287 assertSize(1, docString.getSections());
288 assertEquals("keyword arguments", docString.getSections().get(0).getNormalizedTitle());
292 public void testGoogleDocStringContentDetection() {
293 assertTrue(DocStringUtil.isLikeGoogleDocString(
296 " some user defined section\n" +
304 public void testNumpyEmptySectionIndent() {
305 final NumpyDocString docString = findAndParseNumpyStyleDocString();
306 assertSize(3, docString.getSections());
307 final Section paramSection = docString.getSections().get(0);
308 assertEquals("parameters", paramSection.getNormalizedTitle());
309 assertSize(2, paramSection.getFields());
310 final Section exampleSection = docString.getSections().get(1);
311 assertSize(1, exampleSection.getFields());
312 assertEquals("First sentence.\n" +
313 "Second sentence.", exampleSection.getFields().get(0).getDescription());
314 final Section returnSection = docString.getSections().get(2);
315 assertSize(1, returnSection.getFields());
316 assertEquals("Something", returnSection.getFields().get(0).getType());
319 public void testGoogleParamNamedLikeSection() {
320 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
321 assertSize(1, docString.getSections());
322 final Section paramSection = docString.getSections().get(0);
323 assertSize(2, paramSection.getFields());
324 assertEquals("x", paramSection.getFields().get(0).getName());
325 assertEquals("args", paramSection.getFields().get(1).getName());
328 public void testGoogleNoColonAfterParameter() {
329 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
330 assertSize(1, docString.getSections());
331 final Section paramSection = docString.getSections().get(0);
332 assertSize(2, paramSection.getFields());
333 final SectionField x = paramSection.getFields().get(0);
334 assertEquals("x", x.getName());
335 assertEmpty(x.getType());
336 assertEmpty(x.getDescription());
338 final SectionField y = paramSection.getFields().get(1);
339 assertEquals("y", y.getName());
340 assertEquals("int", y.getType());
341 assertEmpty(y.getDescription());
344 public void testNumpyMultipleReturns() {
345 final NumpyDocString docString = findAndParseNumpyStyleDocString();
346 assertSize(1, docString.getSections());
347 final Section returnSection = docString.getSections().get(0);
348 assertSize(2, returnSection.getFields());
352 public void testNumpyCombinedParamDeclarations() {
353 final NumpyDocString docString = findAndParseNumpyStyleDocString();
354 assertSize(1, docString.getSections());
355 final Section paramSection = docString.getSections().get(0);
356 assertSize(1, paramSection.getFields());
357 final SectionField firstField = paramSection.getFields().get(0);
358 assertSameElements(firstField.getNames(), "x", "y", "args", "kwargs");
359 assertEquals(firstField.getType(), "Any");
360 assertEquals(firstField.getDescription(), "description");
364 public void testGoogleMandatoryIndentationInsideSection() {
365 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
366 assertSize(3, docString.getSections());
367 assertEmpty(docString.getSections().get(0).getFields());
368 assertSize(1, docString.getSections().get(1).getFields());
369 final Section thirdSection = docString.getSections().get(2);
370 assertSize(1, thirdSection.getFields());
371 final SectionField firstExample = thirdSection.getFields().get(0);
372 assertEmpty(firstExample.getName());
373 assertEmpty(firstExample.getType());
374 assertEquals("first line\n" +
375 "second line", firstExample.getDescription());
379 public void testGoogleNoClosingParenthesisAfterParamType() {
380 final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString();
381 assertSize(1, docString.getSections());
382 final List<SectionField> params = docString.getSections().get(0).getFields();
383 assertSize(2, params);
384 assertEquals("Foo", params.get(0).getType());
385 assertEquals("Bar", params.get(1).getType());
388 // PY-17657, PY-16303
389 public void testNotGoogleFormatIfDocstringContainTags() {
390 assertEquals(DocStringFormat.REST, DocStringUtil.guessDocStringFormat("\"\"\"\n" +
391 ":type sub_field: FieldDescriptor | () -> FieldDescriptor\n" +
392 ":param sub_field: The type of field in this collection\n" +
393 " Tip: You can pass a ValueObject class here to ...\n" +
395 " addresses = field.Collection(AddressObject)\n" +
398 assertEquals(DocStringFormat.REST, DocStringUtil.guessDocStringFormat("\"\"\"\n" +
400 " :param Tuple[int, int] name: Some description\n" +
406 protected String getTestDataPath() {
407 return super.getTestDataPath() + "/docstrings";