11import pytest
2+ import sys
23import warnings
4+ from inspect import getsourcelines
35
46from numpydoc import validate
57import numpydoc .tests
@@ -1528,6 +1530,35 @@ def test_bad_docstrings(self, capsys, klass, func, msgs):
15281530 assert msg in " " .join (err [1 ] for err in result ["errors" ])
15291531
15301532
1533+ def decorator (x ):
1534+ """Test decorator."""
1535+ return x
1536+
1537+
1538+ @decorator
1539+ @decorator
1540+ class DecoratorClass :
1541+ """
1542+ Class and methods with decorators.
1543+
1544+ `DecoratorClass` has two decorators, `DecoratorClass.test_no_decorator` has no
1545+ decorator and `DecoratorClass.test_three_decorators` has three decorators.
1546+ `Validator.source_file_def_line` should return the `def` or `class` line number, not
1547+ the line of the first decorator.
1548+ """
1549+
1550+ def test_no_decorator (self ):
1551+ """Test method without decorators."""
1552+ pass
1553+
1554+ @decorator
1555+ @decorator
1556+ @decorator
1557+ def test_three_decorators (self ):
1558+ """Test method with three decorators."""
1559+ pass
1560+
1561+
15311562class TestValidatorClass :
15321563 @pytest .mark .parametrize ("invalid_name" , ["unknown_mod" , "unknown_mod.MyClass" ])
15331564 def test_raises_for_invalid_module_name (self , invalid_name ):
@@ -1544,3 +1575,31 @@ def test_raises_for_invalid_attribute_name(self, invalid_name):
15441575 msg = f"'{ obj_name } ' has no attribute '{ invalid_attr_name } '"
15451576 with pytest .raises (AttributeError , match = msg ):
15461577 numpydoc .validate .Validator ._load_obj (invalid_name )
1578+
1579+ # inspect.getsourcelines does not return class decorators for Python 3.8. This was
1580+ # fixed starting with 3.9: https://github.com/python/cpython/issues/60060
1581+ @pytest .mark .parametrize (
1582+ ["decorated_obj" , "def_line" ],
1583+ [
1584+ [
1585+ "numpydoc.tests.test_validate.DecoratorClass" ,
1586+ getsourcelines (DecoratorClass )[- 1 ]
1587+ + (2 if sys .version_info .minor > 8 else 0 ),
1588+ ],
1589+ [
1590+ "numpydoc.tests.test_validate.DecoratorClass.test_no_decorator" ,
1591+ getsourcelines (DecoratorClass .test_no_decorator )[- 1 ],
1592+ ],
1593+ [
1594+ "numpydoc.tests.test_validate.DecoratorClass.test_three_decorators" ,
1595+ getsourcelines (DecoratorClass .test_three_decorators )[- 1 ] + 3 ,
1596+ ],
1597+ ],
1598+ )
1599+ def test_source_file_def_line_with_decorators (self , decorated_obj , def_line ):
1600+ doc = numpydoc .validate .Validator (
1601+ numpydoc .docscrape .get_doc_object (
1602+ numpydoc .validate .Validator ._load_obj (decorated_obj )
1603+ )
1604+ )
1605+ assert doc .source_file_def_line == def_line
0 commit comments