diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py index f0e42ebd..d6544b21 100644 --- a/numpydoc/docscrape.py +++ b/numpydoc/docscrape.py @@ -426,7 +426,13 @@ def _error_location(self, msg, error=True): filename = inspect.getsourcefile(self._obj) except TypeError: filename = None - msg += f" in the docstring of {self._obj.__name__}" + # Make UserWarning more descriptive via object introspection. + # Skip if introspection fails + name = getattr(self._obj, '__name__', None) + if name is None: + name = getattr(getattr(self._obj, '__class__', None), '__name__', None) + if name is not None: + msg += f" in the docstring of {name}" msg += f" in {filename}." if filename else "" if error: raise ValueError(msg) diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index 00ed9f7e..83a8d5b3 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -1522,6 +1522,32 @@ def __init__(self, a, b): line_by_line_compare(str(doc), xref_doc_txt_expected) +def test__error_location_no_name_attr(): + """ + Ensure that NumpyDocString._error_location doesn't fail when self._obj + does not have a __name__ attr. + + See gh-362 + """ + from collections.abc import Callable + + # Create a Callable that doesn't have a __name__ attribute + class Foo(): + def __call__(self): + pass + + + foo = Foo() # foo is a Callable, but no a function instance + assert isinstance(foo, Callable) + + # Create an NumpyDocString instance to call the _error_location method + nds = get_doc_object(foo) + + msg = "Potentially wrong underline length.*Foo.*" + with pytest.raises(ValueError, match=msg): + nds._error_location(msg=msg) + + if __name__ == "__main__": import pytest pytest.main()