diff --git a/newsfragments/2987.issue.rst b/newsfragments/2987.issue.rst new file mode 100644 index 0000000000..4c0adb1540 --- /dev/null +++ b/newsfragments/2987.issue.rst @@ -0,0 +1 @@ +Fix crash when importing trio in embedded Python on Windows, and other installs that remove docstrings. diff --git a/src/trio/_path.py b/src/trio/_path.py index cb19008d54..907b0203f6 100644 --- a/src/trio/_path.py +++ b/src/trio/_path.py @@ -38,12 +38,12 @@ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: return await run_sync(partial(fn, *args, **kwargs)) update_wrapper(wrapper, wrapped) - assert wrapped.__doc__ is not None - wrapper.__doc__ = ( - f"Like :meth:`~{wrapped.__module__}.{wrapped.__qualname__}`, but async.\n" - f"\n" - f"{cleandoc(wrapped.__doc__)}\n" - ) + if wrapped.__doc__: + wrapper.__doc__ = ( + f"Like :meth:`~{wrapped.__module__}.{wrapped.__qualname__}`, but async.\n" + f"\n" + f"{cleandoc(wrapped.__doc__)}\n" + ) return wrapper return decorator @@ -76,23 +76,23 @@ def _wrap_method_path_iterable( def wrapper(self: PathT, /, *args: P.args, **kwargs: P.kwargs) -> Iterable[PathT]: return map(self.__class__, [*fn(self._wrapped_cls(self), *args, **kwargs)]) - assert wrapper.__doc__ is not None - wrapper.__doc__ += ( - f"\n" - f"This is an async method that returns a synchronous iterator, so you\n" - f"use it like:\n" - f"\n" - f".. code:: python\n" - f"\n" - f" for subpath in await mypath.{fn.__name__}():\n" - f" ...\n" - f"\n" - f".. note::\n" - f"\n" - f" The iterator is loaded into memory immediately during the initial\n" - f" call (see `issue #501\n" - f" `__ for discussion).\n" - ) + if wrapper.__doc__: + wrapper.__doc__ += ( + f"\n" + f"This is an async method that returns a synchronous iterator, so you\n" + f"use it like:\n" + f"\n" + f".. code:: python\n" + f"\n" + f" for subpath in await mypath.{fn.__name__}():\n" + f" ...\n" + f"\n" + f".. note::\n" + f"\n" + f" The iterator is loaded into memory immediately during the initial\n" + f" call (see `issue #501\n" + f" `__ for discussion).\n" + ) return wrapper diff --git a/src/trio/_tests/test_path.py b/src/trio/_tests/test_path.py index 21ebefc90d..db7aebf2df 100644 --- a/src/trio/_tests/test_path.py +++ b/src/trio/_tests/test_path.py @@ -252,3 +252,21 @@ async def test_classmethods() -> None: # Wrapped method has docstring assert trio.Path.home.__doc__ + + +@pytest.mark.parametrize( + "wrapper", + [ + trio._path._wraps_async, + trio._path._wrap_method, + trio._path._wrap_method_path, + trio._path._wrap_method_path_iterable, + ], +) +def test_wrapping_without_docstrings( + wrapper: Callable[[Callable[[], None]], Callable[[], None]] +) -> None: + @wrapper + def func_without_docstring() -> None: ... # pragma: no cover + + assert func_without_docstring.__doc__ is None