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