Skip to content

Commit 86cf43e

Browse files
author
Coding Agent
committed
https://github.com/Penify-dev/PyDocSmith.git
1 parent d1ac6da commit 86cf43e

1 file changed

Lines changed: 298 additions & 0 deletions

File tree

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
"""Integration tests for PyDocSmith functionality."""
2+
3+
import pytest
4+
from PyDocSmith import (
5+
DocstringStyle,
6+
combine_docstrings,
7+
compose,
8+
detect_docstring_style,
9+
parse,
10+
parse_from_object,
11+
)
12+
13+
14+
def test_round_trip_rest():
15+
"""Test round-trip parsing and composing for reST style."""
16+
original_docstring = """
17+
Short description
18+
19+
Long description
20+
21+
:param spam: spam desc
22+
:param bla: bla desc
23+
:raises ValueError: exc desc
24+
:returns: ret desc
25+
"""
26+
27+
parsed = parse(original_docstring, style=DocstringStyle.REST)
28+
composed = compose(parsed, style=DocstringStyle.REST)
29+
30+
# Parse the composed docstring again
31+
reparsed = parse(composed, style=DocstringStyle.REST)
32+
33+
assert reparsed.short_description == parsed.short_description
34+
assert reparsed.long_description == parsed.long_description
35+
assert len(reparsed.params) == len(parsed.params)
36+
assert len(reparsed.raises) == len(parsed.raises)
37+
assert reparsed.returns.description == parsed.returns.description
38+
39+
40+
def test_round_trip_google():
41+
"""Test round-trip parsing and composing for Google style."""
42+
original_docstring = """Short description
43+
44+
Long description
45+
46+
Args:
47+
spam: spam desc
48+
bla (int): bla desc
49+
50+
Raises:
51+
ValueError: exc desc
52+
53+
Returns:
54+
tuple: ret desc
55+
"""
56+
57+
parsed = parse(original_docstring, style=DocstringStyle.GOOGLE)
58+
composed = compose(parsed, style=DocstringStyle.GOOGLE)
59+
60+
reparsed = parse(composed, style=DocstringStyle.GOOGLE)
61+
62+
assert reparsed.short_description == parsed.short_description
63+
assert reparsed.long_description == parsed.long_description
64+
assert len(reparsed.params) == len(parsed.params)
65+
assert len(reparsed.raises) == len(parsed.raises)
66+
assert reparsed.returns.description == parsed.returns.description
67+
68+
69+
def test_round_trip_numpydoc():
70+
"""Test round-trip parsing and composing for NumPy style."""
71+
original_docstring = """Short description
72+
73+
Long description
74+
75+
Parameters
76+
----------
77+
spam
78+
spam desc
79+
bla : int
80+
bla desc
81+
82+
Raises
83+
------
84+
ValueError
85+
exc desc
86+
87+
Returns
88+
-------
89+
tuple
90+
ret desc
91+
"""
92+
93+
parsed = parse(original_docstring, style=DocstringStyle.NUMPYDOC)
94+
composed = compose(parsed, style=DocstringStyle.NUMPYDOC)
95+
96+
reparsed = parse(composed, style=DocstringStyle.NUMPYDOC)
97+
98+
assert reparsed.short_description == parsed.short_description
99+
assert reparsed.long_description == parsed.long_description
100+
assert len(reparsed.params) == len(parsed.params)
101+
assert len(reparsed.raises) == len(parsed.raises)
102+
assert reparsed.returns.description == parsed.returns.description
103+
104+
105+
def test_round_trip_epydoc():
106+
"""Test round-trip parsing and composing for Epydoc style."""
107+
original_docstring = """Short description
108+
109+
Long description
110+
111+
@param spam: spam desc
112+
@param bla: bla desc
113+
@raise ValueError: exc desc
114+
@return: ret desc
115+
"""
116+
117+
parsed = parse(original_docstring, style=DocstringStyle.EPYDOC)
118+
composed = compose(parsed, style=DocstringStyle.EPYDOC)
119+
120+
reparsed = parse(composed, style=DocstringStyle.EPYDOC)
121+
122+
assert reparsed.short_description == parsed.short_description
123+
assert reparsed.long_description == parsed.long_description
124+
assert len(reparsed.params) == len(parsed.params)
125+
assert len(reparsed.raises) == len(parsed.raises)
126+
assert reparsed.returns.description == parsed.returns.description
127+
128+
129+
def test_auto_detect_and_parse():
130+
"""Test automatic style detection followed by parsing."""
131+
docstrings = [
132+
("""
133+
Short desc
134+
135+
:param x: param x
136+
:returns: result
137+
""", DocstringStyle.REST),
138+
("""Short desc
139+
140+
Args:
141+
x: param x
142+
143+
Returns:
144+
result
145+
""", DocstringStyle.GOOGLE),
146+
("""Short desc
147+
148+
Parameters
149+
----------
150+
x
151+
param x
152+
153+
Returns
154+
-------
155+
result
156+
""", DocstringStyle.NUMPYDOC),
157+
("""Short desc
158+
159+
@param x: param x
160+
@return: result
161+
""", DocstringStyle.EPYDOC),
162+
]
163+
164+
for docstring_text, expected_style in docstrings:
165+
detected_style = detect_docstring_style(docstring_text)
166+
assert detected_style == expected_style
167+
168+
parsed = parse(docstring_text)
169+
assert parsed.style == expected_style
170+
171+
172+
def test_parse_from_object_function():
173+
"""Test parse_from_object with a function."""
174+
def sample_function(param1: str, param2: int = 42):
175+
"""Sample function docstring.
176+
177+
Args:
178+
param1: First parameter
179+
param2: Second parameter with default
180+
181+
Returns:
182+
str: Combined result
183+
"""
184+
return f"{param1}_{param2}"
185+
186+
parsed = parse_from_object(sample_function)
187+
188+
assert parsed.short_description == "Sample function docstring."
189+
assert len(parsed.params) == 2
190+
assert parsed.params[0].arg_name == "param1"
191+
assert parsed.params[0].description == "First parameter"
192+
assert parsed.params[1].arg_name == "param2"
193+
assert parsed.params[1].description == "Second parameter with default"
194+
assert parsed.returns.description == "Combined result"
195+
196+
197+
def test_parse_from_object_class():
198+
"""Test parse_from_object with a class including attributes."""
199+
class SampleClass:
200+
"""A sample class for testing.
201+
202+
This class demonstrates attribute docstrings.
203+
"""
204+
205+
attr1: str
206+
"""First attribute"""
207+
208+
attr2: int = 10
209+
"""Second attribute with default"""
210+
211+
def method(self, x: float):
212+
"""Method docstring.
213+
214+
Args:
215+
x: Input value
216+
217+
Returns:
218+
float: Processed value
219+
"""
220+
return x * 2
221+
222+
parsed = parse_from_object(SampleClass)
223+
224+
assert "sample class" in parsed.short_description.lower()
225+
assert len(parsed.params) == 2 # attributes
226+
assert parsed.params[0].arg_name == "attr1"
227+
assert parsed.params[0].description == "First attribute"
228+
assert parsed.params[1].arg_name == "attr2"
229+
assert parsed.params[1].description == "Second attribute with default"
230+
231+
232+
def test_combine_docstrings():
233+
"""Test combining multiple docstrings."""
234+
doc1 = """First docstring.
235+
236+
Args:
237+
x: First param
238+
"""
239+
240+
doc2 = """Second docstring.
241+
242+
Args:
243+
y: Second param
244+
245+
Returns:
246+
result
247+
"""
248+
249+
combined = combine_docstrings([doc1, doc2])
250+
251+
parsed = parse(combined, style=DocstringStyle.GOOGLE)
252+
253+
assert "First docstring" in parsed.short_description
254+
assert "Second docstring" in parsed.long_description or "Second docstring" in parsed.short_description
255+
assert len(parsed.params) == 2
256+
assert any(p.arg_name == "x" for p in parsed.params)
257+
assert any(p.arg_name == "y" for p in parsed.params)
258+
assert parsed.returns is not None
259+
260+
261+
def test_error_handling_integration():
262+
"""Test error handling in integration scenarios."""
263+
# Test with malformed docstring that might cause issues
264+
malformed = """
265+
:param invalid syntax here
266+
:returns: something
267+
"""
268+
269+
# Should not crash, should either parse or raise appropriate error
270+
try:
271+
parsed = parse(malformed)
272+
assert parsed is not None
273+
except Exception:
274+
# If it raises, it should be a ParseError
275+
pass
276+
277+
278+
def test_cross_style_composition():
279+
"""Test composing a parsed docstring in a different style."""
280+
google_doc = """Function description.
281+
282+
Args:
283+
param1: First parameter
284+
param2 (int): Second parameter
285+
286+
Returns:
287+
str: Result string
288+
"""
289+
290+
parsed = parse(google_doc, style=DocstringStyle.GOOGLE)
291+
292+
# Compose in reST style
293+
rest_composed = compose(parsed, style=DocstringStyle.REST)
294+
295+
# Should be valid reST
296+
reparsed = parse(rest_composed, style=DocstringStyle.REST)
297+
assert reparsed.short_description == parsed.short_description
298+
assert len(reparsed.params) == len(parsed.params)

0 commit comments

Comments
 (0)