Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add async support for temporary file handling. Closes #344 #867

Merged
merged 30 commits into from
Mar 2, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
beb8285
feat: Add async support for temporary file handling. Closes #344
11kkw Feb 10, 2025
07730ef
Merge branch 'master' into async-tempfile-support
agronholm Feb 10, 2025
93c9ae4
Fix: Apply requested changes from @agronholm
11kkw Feb 11, 2025
546a462
Merge branch 'master' into async-tempfile-support
agronholm Feb 11, 2025
684176b
fix: add blank lines after control blocks, fix Pyright errors, and ig…
11kkw Feb 11, 2025
2c89b61
fix(tempfile): apply review feedback and improve async tempfile
11kkw Feb 12, 2025
2e6ea50
Update src/anyio/_core/_tempfile.py
11kkw Feb 14, 2025
db3c2cd
Update src/anyio/_core/_tempfile.py
11kkw Feb 14, 2025
ad12c85
tempfile: update type annotations, fix Py3.14 compatibility, and upda…
11kkw Feb 14, 2025
50c498f
Merge branch 'master' into async-tempfile-support
agronholm Feb 15, 2025
b7a9722
Removed redundant py3.14 fixes
agronholm Feb 15, 2025
2a1a1cf
Update src/anyio/_core/_tempfile.py
11kkw Feb 16, 2025
e608cdc
Update src/anyio/_core/_tempfile.py
11kkw Feb 16, 2025
4cdadc6
Update tests/test_tempfile.py
11kkw Feb 16, 2025
bd36d3c
Update tests/test_tempfile.py
11kkw Feb 16, 2025
86719ec
Update tests/test_tempfile.py
11kkw Feb 16, 2025
c129057
Update tests/test_tempfile.py
11kkw Feb 16, 2025
6551774
Update tests/test_tempfile.py
11kkw Feb 16, 2025
f730c72
Update tests/test_tempfile.py
11kkw Feb 16, 2025
4dc6c71
Update tests/test_tempfile.py
11kkw Feb 16, 2025
bd61572
Update tests/test_tempfile.py
11kkw Feb 16, 2025
1509c28
docs: Include TemporaryFile-related classes in API reference
11kkw Feb 16, 2025
78bb1d1
api.rst update
11kkw Feb 16, 2025
9eaa5c5
Merge branch 'master' into async-tempfile-support
agronholm Feb 22, 2025
77e7259
Reimplemented SpooledTemporaryFile and improved type annotations
agronholm Feb 23, 2025
058ab8a
Updated tests
agronholm Feb 23, 2025
e88499e
Reduced the number of mypy errors
agronholm Mar 1, 2025
e57dd1d
Silenced the remaining mypy errors
agronholm Mar 1, 2025
8b59b66
Merge branch 'master' into async-tempfile-support
agronholm Mar 1, 2025
7d8f4b7
Added some finishing touches
agronholm Mar 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The manual
subprocesses
subinterpreters
fileio
tempfile
signals
testing
api
Expand Down
119 changes: 119 additions & 0 deletions docs/tempfile.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
Asynchronous Temporary File and Directory
=========================================

.. py:currentmodule:: anyio

This module provides asynchronous wrappers for handling temporary files and directories
using the :mod:`tempfile` module. The asynchronous methods execute blocking operations in worker threads.

Temporary File
--------------

:class:`TemporaryFile` creates a temporary file that is automatically deleted upon closure.

**Example:**

.. code-block:: python

from anyio import TemporaryFile, run

async def main():
async with TemporaryFile(mode="w+") as f:
await f.write("Temporary file content")
await f.seek(0)
print(await f.read()) # Output: Temporary file content

run(main)

Named Temporary File
--------------------

:class:`NamedTemporaryFile` works similarly to :class:`TemporaryFile`, but the file has a visible name in the filesystem.

**Example:**

.. code-block:: python

from anyio import NamedTemporaryFile, run

async def main():
async with NamedTemporaryFile(mode="w+", delete=True) as f:
print(f"Temporary file name: {f.name}")
await f.write("Named temp file content")
await f.seek(0)
print(await f.read())

run(main)

Spooled Temporary File
----------------------

:class:`SpooledTemporaryFile` is useful when temporary data is small and should be kept in memory rather than written to disk.

**Example:**

.. code-block:: python

from anyio import SpooledTemporaryFile, run

async def main():
async with SpooledTemporaryFile(max_size=1024, mode="w+") as f:
await f.write("Spooled temp file content")
await f.seek(0)
print(await f.read())

run(main)

Temporary Directory
-------------------

The :class:`TemporaryDirectory` provides an asynchronous way to create temporary directories.

**Example:**

.. code-block:: python

from anyio import TemporaryDirectory, run

async def main():
async with TemporaryDirectory() as temp_dir:
print(f"Temporary directory path: {temp_dir}")

run(main)

Low-Level Temporary File and Directory Creation
-----------------------------------------------

For more control, the module provides lower-level functions:

- :func:`mkstemp` - Creates a temporary file and returns a tuple of file descriptor and path.
- :func:`mkdtemp` - Creates a temporary directory and returns the directory path.
- :func:`gettempdir` - Returns the path of the default temporary directory.
- :func:`gettempdirb` - Returns the path of the default temporary directory in bytes.

**Example:**

.. code-block:: python

from anyio import mkstemp, mkdtemp, gettempdir, run
import os

async def main():
fd, path = await mkstemp(suffix=".txt", prefix="mkstemp_", text=True)
print(f"Created temp file: {path}")

temp_dir = await mkdtemp(prefix="mkdtemp_")
print(f"Created temp dir: {temp_dir}")

print(f"Default temp dir: {await gettempdir()}")

os.remove(path)

run(main)

.. note::
Using these functions requires manual cleanup of the created files and directories.

.. seealso::

- Python Standard Library: :mod:`tempfile` (`official documentation <https://docs.python.org/3/library/tempfile.html>`_)
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ extend-select = [
[tool.ruff.lint.isort]
"required-imports" = ["from __future__ import annotations"]

[tool.ruff.lint.per-file-ignores]
"tests/test_tempfile.py" = ["ASYNC230"]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which open call is blocking?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one in test_mkstemp().


[tool.mypy]
python_version = "3.13"
strict = true
Expand Down
8 changes: 8 additions & 0 deletions src/anyio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@
from ._core._tasks import current_effective_deadline as current_effective_deadline
from ._core._tasks import fail_after as fail_after
from ._core._tasks import move_on_after as move_on_after
from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile
from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile
from ._core._tempfile import TemporaryDirectory as TemporaryDirectory
from ._core._tempfile import TemporaryFile as TemporaryFile
from ._core._tempfile import gettempdir as gettempdir
from ._core._tempfile import gettempdirb as gettempdirb
from ._core._tempfile import mkdtemp as mkdtemp
from ._core._tempfile import mkstemp as mkstemp
from ._core._testing import TaskInfo as TaskInfo
from ._core._testing import get_current_task as get_current_task
from ._core._testing import get_running_tasks as get_running_tasks
Expand Down
Loading
Loading