diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst
index 2f9b724da..b8e21dc24 100644
--- a/docs/versionhistory.rst
+++ b/docs/versionhistory.rst
@@ -13,6 +13,8 @@ This library adheres to `Semantic Versioning 2.0 `_.
``Could not create 2 listeners with a consistent port`` when an ephemeral port is
requested and IPv6 is enabled and the dual-stack path is not available or a specific
local host name was given
+- Fixed ``AsyncFile`` not shielding against cancellation while closing
+ (`#1314 `_)
**4.15.1**
diff --git a/src/anyio/_core/_fileio.py b/src/anyio/_core/_fileio.py
index 498eef41d..fc0c36fab 100644
--- a/src/anyio/_core/_fileio.py
+++ b/src/anyio/_core/_fileio.py
@@ -28,6 +28,7 @@
from .. import to_thread
from ..abc import AsyncResource
from ._synchronization import CapacityLimiter
+from ._tasks import CancelScope
if sys.version_info >= (3, 11):
from typing import Self
@@ -114,7 +115,8 @@ async def __aiter__(self) -> AsyncIterator[AnyStr]:
break
async def aclose(self) -> None:
- return await to_thread.run_sync(self._fp.close, limiter=self._limiter)
+ with CancelScope(shield=True):
+ await to_thread.run_sync(self._fp.close, limiter=self._limiter)
async def read(self, size: int = -1) -> AnyStr:
return await to_thread.run_sync(self._fp.read, size, limiter=self._limiter)
diff --git a/tests/test_fileio.py b/tests/test_fileio.py
index 2b7f95699..3cadb1a22 100644
--- a/tests/test_fileio.py
+++ b/tests/test_fileio.py
@@ -12,7 +12,14 @@
from _pytest.fixtures import FixtureRequest
from _pytest.tmpdir import TempPathFactory
-from anyio import AsyncFile, CapacityLimiter, Path, open_file, wrap_file
+from anyio import (
+ AsyncFile,
+ CancelScope,
+ CapacityLimiter,
+ Path,
+ open_file,
+ wrap_file,
+)
@pytest.fixture(params=[False, True])
@@ -86,6 +93,14 @@ async def test_wrap_file(
assert path.read_text() == "dummydata"
+ async def test_shielded_aclose(self, tmp_path: pathlib.Path) -> None:
+ async with await open_file(tmp_path / "foo", "wb") as f:
+ with CancelScope() as scope:
+ scope.cancel()
+ await f.aclose()
+
+ assert f.closed
+
class TestPath:
@pytest.fixture