Skip to content

Commit 10f8ae6

Browse files
authored
Unwrap decorated objects for YD01 validation check (#541)
* ENH: decorator unwrapping for Yields validation. * Add test for unwrapped Yields validation.
1 parent 01b4be7 commit 10f8ae6

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

numpydoc/tests/test_validate.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
import sys
33
import warnings
44
from contextlib import nullcontext
5-
from functools import cached_property, partial
5+
from functools import cached_property, partial, wraps
66
from inspect import getsourcelines, getsourcefile
77

88
from numpydoc import validate
9+
from numpydoc.validate import Validator
10+
from numpydoc.docscrape import get_doc_object
911
import numpydoc.tests
1012

1113

@@ -1692,3 +1694,43 @@ def test_source_file_name_with_properties(self, property, file_name):
16921694
)
16931695
)
16941696
assert doc.source_file_name == file_name
1697+
1698+
1699+
def test_is_generator_validation_with_decorator():
1700+
"""Ensure that the check for a Yields section when an object is a generator
1701+
(YD01) works with decorated generators."""
1702+
1703+
def tinsel(f):
1704+
@wraps(f)
1705+
def wrapper(*args, **kwargs):
1706+
return f(*args, **kwargs)
1707+
1708+
return wrapper
1709+
1710+
def foo():
1711+
"""A simple generator"""
1712+
yield from range(10)
1713+
1714+
@tinsel
1715+
def bar():
1716+
"""Generator wrapped once"""
1717+
yield from range(10)
1718+
1719+
@tinsel
1720+
@tinsel
1721+
@tinsel
1722+
def baz():
1723+
"""Generator wrapped multiple times"""
1724+
yield from range(10)
1725+
1726+
# foo without wrapper is a generator
1727+
v = Validator(get_doc_object(foo))
1728+
assert v.is_generator_function
1729+
1730+
# Wrapped once
1731+
v = Validator(get_doc_object(bar))
1732+
assert v.is_generator_function
1733+
1734+
# Wrapped multiple times
1735+
v = Validator(get_doc_object(baz))
1736+
assert v.is_generator_function

numpydoc/validate.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@
112112
IGNORE_COMMENT_PATTERN = re.compile("(?:.* numpydoc ignore[=|:] ?)(.+)")
113113

114114

115+
def _unwrap(obj):
116+
"""Iteratively traverse obj.__wrapped__ until first non-wrapped obj found."""
117+
while hasattr(obj, "__wrapped__"):
118+
obj = obj.__wrapped__
119+
return obj
120+
121+
115122
# This function gets called once per function/method to be validated.
116123
# We have to balance memory usage with performance here. It shouldn't be too
117124
# bad to store these `dict`s (they should be rare), but to be safe let's keep
@@ -273,7 +280,7 @@ def is_function_or_method(self):
273280

274281
@property
275282
def is_generator_function(self):
276-
return inspect.isgeneratorfunction(self.obj)
283+
return inspect.isgeneratorfunction(_unwrap(self.obj))
277284

278285
@property
279286
def source_file_name(self):

0 commit comments

Comments
 (0)