diff --git a/trio/_core/_run.py b/trio/_core/_run.py index fb239304ef..8b654a90b3 100644 --- a/trio/_core/_run.py +++ b/trio/_core/_run.py @@ -37,7 +37,7 @@ ) from .. import _core from .._deprecate import deprecated -from .._util import Final +from .._util import Final, NoPublicConstructor # At the bottom of this file there's also some "clever" code that generates # wrapper functions for runner and io manager methods, and adds them to @@ -717,7 +717,7 @@ class NurseryManager: async def __aenter__(self): self._scope = CancelScope() self._scope.__enter__() - self._nursery = Nursery(current_task(), self._scope) + self._nursery = Nursery._create(current_task(), self._scope) return self._nursery @enable_ki_protection @@ -761,7 +761,7 @@ def open_nursery(): return NurseryManager() -class Nursery: +class Nursery(metaclass=NoPublicConstructor): def __init__(self, parent_task, cancel_scope): self._parent_task = parent_task parent_task._child_nurseries.append(self) diff --git a/trio/_core/tests/test_run.py b/trio/_core/tests/test_run.py index 5e356e5f3b..16b56c00ec 100644 --- a/trio/_core/tests/test_run.py +++ b/trio/_core/tests/test_run.py @@ -2142,6 +2142,30 @@ async def inner(): _core.run(inner) +def test_Nursery_init(): + check_Nursery_error = pytest.raises( + TypeError, match='no public constructor available' + ) + + with check_Nursery_error: + _core._run.Nursery(None, None) + + +async def test_Nursery_private_init(): + # context manager creation should not raise + async with _core.open_nursery() as nursery: + assert False == nursery._closed + + +def test_Nursery_subclass(): + with pytest.raises( + TypeError, match='`Nursery` does not support subclassing' + ): + + class Subclass(_core._run.Nursery): + pass + + def test_Cancelled_init(): check_Cancelled_error = pytest.raises( TypeError, match='no public constructor available'