Skip to main content

SleepFake Logo

CI PyPI version Python versions Coverage 100% Free-threading ready License: MIT

💤 SleepFake: Time Travel for Your Tests

Ever wish your tests could skip the waiting but keep correct time behavior? SleepFake patches time.sleep and asyncio.sleep so tests return instantly while frozen time moves forward exactly as requested. Async timeouts (asyncio.wait_for, asyncio.timeout, loop.call_later) fire on the fake clock too.

📦 Install

pip install sleepfake

Python 3.10 to 3.15, including free-threaded builds (3.13t+). The pytest plugin registers itself.

⚡ Quick start: global autouse

Make SleepFake apply to every test. Add to pyproject.toml:

[tool.pytest.ini_options]
sleepfake_autouse = true

Now regular tests skip sleeps:

import time


def test_retry():
    start = time.time()
    time.sleep(30)  # returns instantly
    assert time.time() - start >= 30

Async works the same way, timeouts included:

import asyncio

import pytest


async def test_polling():
    start = asyncio.get_running_loop().time()
    await asyncio.sleep(10)  # returns instantly
    assert asyncio.get_running_loop().time() - start >= 10


async def test_gives_up():
    with pytest.raises(TimeoutError):
        await asyncio.wait_for(asyncio.Event().wait(), timeout=60)  # ~20 ms, not 60 s

pytest-asyncio users: add asyncio_mode = "auto" (or mark tests with @pytest.mark.asyncio) so pytest collects async tests.

🧭 Choose your usage style

Use case Best option
Apply everywhere (most teams) Global autouse (sleepfake_autouse = true or --sleepfake)
Per-test explicit control sleepfake fixture
Decoration-style usage @pytest.mark.sleepfake
Non-pytest scripts / direct control SleepFake context manager

All pytest styles share one instance per test: combining them never patches twice.

📚 Usage

Context manager
import asyncio
import time

from sleepfake import SleepFake

# Sync
with SleepFake():
    start = time.time()
    time.sleep(10)  # returns instantly
    assert time.time() - start >= 10


# Async: `async with` or plain `with` both work
async def main():
    async with SleepFake():
        start = asyncio.get_running_loop().time()
        await asyncio.sleep(5)  # returns instantly
        assert asyncio.get_running_loop().time() - start >= 5

Keep some modules on real clocks with ignore:

with SleepFake(ignore=["my_project.telemetry"]):
    ...
Fixture (sleepfake)
import asyncio
import time


def test_retry_logic(sleepfake):
    start = time.time()
    time.sleep(30)  # instantly skipped
    assert time.time() - start >= 30


async def test_polling(sleepfake):
    start = asyncio.get_running_loop().time()
    await asyncio.gather(asyncio.sleep(1), asyncio.sleep(5), asyncio.sleep(3))
    # All three complete instantly; the clock sits at +5 s
    assert asyncio.get_running_loop().time() - start >= 5

The fixture yields the active SleepFake; sleepfake.mock_sleep(60) advances the clock by hand.

Deprecated: asleepfake still works but warns. Use sleepfake for sync and async tests.

Marker (@pytest.mark.sleepfake)
import time

import pytest


@pytest.mark.sleepfake
def test_marked():
    start = time.time()
    time.sleep(100)
    assert time.time() - start >= 100

Works on async tests, classes and modules (pytestmark) too.

Global autouse, opt-out and ignores

Enable it in config or on the command line:

# pyproject.toml
[tool.pytest.ini_options]
sleepfake_autouse = true
sleepfake_ignore = ["my_project.telemetry", "my_project.metrics"]
pytest --sleepfake --sleepfake-ignore my_project.telemetry

Opt a single test out with @pytest.mark.no_sleepfake (it has no effect if the test explicitly requests the sleepfake fixture).

Directory-scoped ignores go in a conftest.py; the nearest one wins:

# conftest.py
pytest_sleepfake_ignore = ["my_project.telemetry"]

Function-scoped fixtures are set up and torn down with the fake active; session-, module- and class-scoped fixtures stay on real time.

Async timeouts and the autojump threshold

asyncio.sleep always wakes instantly. Loop timers (asyncio.wait_for, asyncio.timeout, loop.call_later) fire once the event loop has been idle for autojump_threshold real seconds (default 0.02). That short grace period lets real I/O, such as a local test server, answer before a timeout is forced.

async def test_timeout(sleepfake):
    with pytest.raises(TimeoutError):
        async with asyncio.timeout(2):
            await asyncio.sleep(10)  # the clock stops at +2 s, like real asyncio

Tune it per instance or per project:

SleepFake(autojump_threshold=0)  # jump as soon as the loop is idle
SleepFake(autojump_threshold=math.inf)  # never jump: timers only fire when sleeps move the clock
[tool.pytest.ini_options]
sleepfake_autojump_threshold = "0.5"

🛠️ Options reference

Where Option Purpose
SleepFake(...) ignore: list[str] Module prefixes that stay on real clocks.
SleepFake(...) autojump_threshold: float Real idle seconds before loop timers fire (default 0.02, 0, math.inf).
pytest config sleepfake_autouse = true Apply SleepFake to every test.
pytest config sleepfake_ignore Module prefixes that stay on real clocks, for every test.
pytest config sleepfake_autojump_threshold Same as the constructor argument, for every test.
pytest CLI --sleepfake Same as sleepfake_autouse = true.
pytest CLI --sleepfake-ignore MODULE Add an ignored prefix (repeatable).
conftest.py pytest_sleepfake_ignore Ignored prefixes for that directory subtree (a string or an iterable).
marker @pytest.mark.sleepfake Apply SleepFake to one test, class or module.
marker @pytest.mark.no_sleepfake Opt one test out of global autouse.

Every ignore list is merged with DEFAULT_IGNORE = ["_pytest.timing", "pytest_timeout"], which keeps pytest's --durations and pytest-timeout on real clocks. Disable the plugin with -p no:sleepfake.

🧪 How it works

Aspect Detail
Clock freezegun freezes time.time, time.monotonic, datetime.now... and so the event loop's clock
Sync sleep time.sleep(n) ticks the frozen clock by n (thread-safe)
Async sleep Once the event loop is idle, the clock jumps to the earliest pending deadline and that sleep wakes, so concurrent sleeps resolve in order
Loop timers BaseEventLoop.call_at is patched; after autojump_threshold real idle seconds the clock jumps to the next timer
Module aliases from time import sleep / from asyncio import sleep bindings in sys.modules are swapped on entry and restored on exit
Nesting Contexts nest; the innermost one drives the clock

⚠️ Limitations

  • Local bindings. A sleep captured in a local variable before the context starts (_sleep = time.sleep) keeps calling the real function.
  • Real I/O under a timeout. If real I/O takes longer than autojump_threshold inside a wait_for/timeout, the timeout fires on the fake clock. Raise the threshold, or set it to math.inf.
  • Other event loops. Timer autojump reads asyncio's pure-Python loop internals. On other loops (e.g. uvloop) asyncio.sleep is still faked, but loop timers need real time.
  • Timer precision. A timer jump lands 1 µs after the deadline: asyncio only runs a timer once the clock is strictly past it.
  • Shared clock. The frozen clock is global: every thread and every event loop sees the same time.

🆚 Alternatives

Tool Fakes time.sleep Fakes asyncio.sleep / loop timers Freezes datetime / time.time
SleepFake ✅ ✅ ✅ (via freezegun)
freezegun ❌ (really sleeps) ❌ ✅
time-machine ❌ ❌ ✅
looptime ❌ ✅ (asyncio loop time) ❌

🤝 Contributing

make dev-install   # uv sync + prek git hooks
make test-all      # ruff + ty, then tests
make cov           # tests under coverage (100% required)
make test-all-python  # 3.10 to 3.15 and 3.14t

PRs and issues welcome. See CHANGELOG.md for release notes.

Metadata

Release files for sleepfake 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sleepfake 2.0.0
File Size Uploaded
sleepfake-2.0.0.tar.gz 14.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sleepfake 2.0.0
File Interpreter ABI Platform
sleepfake-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 28.9 kB

Release files / sleepfake-2.0.0.tar.gz

Download URL sleepfake-2.0.0.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
b6af5ee4408f1d203ec592fd5225ba8a39751f69d05988797002432226e675db
BLAKE2b-256 checksum
How to use checksums
d6dcb6fcd9db0fb2ecd710615cbf2f49bb109f7d318f3be9ec8faff44891b2c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / sleepfake-2.0.0-py3-none-any.whl

Download URL sleepfake-2.0.0-py3-none-any.whl
Size 14.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0296dd9b643edd0ebf63c28eb0f5006f9213ef5e9ed26a558769c51c3fe7b237
BLAKE2b-256 checksum
How to use checksums
922a7c2d879a4b54b4be1bca61ad868d2b525ac786f289897c45b00e4cd579e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.2

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page