Fake the time.sleep/asyncio.sleep function during tests.
Project description
💤 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.
🚀 Why teams adopt SleepFake fast
- Zero waiting in sync and async tests
- Minimal setup (one install + one config toggle)
- Realistic behavior for timeouts, task groups, and ordering
📦 Install (30 seconds)
pip install sleepfake
⚡ Quick start (recommended): global autouse
If you want instant wins with almost no boilerplate, make SleepFake apply to every test.
Add to pyproject.toml:
[tool.pytest.ini_options]
sleepfake_autouse = true
Now regular tests automatically 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:
import asyncio
async def test_polling():
start = asyncio.get_running_loop().time()
await asyncio.sleep(10) # returns instantly
assert asyncio.get_running_loop().time() - start >= 10
✅ Result: your suite keeps time-based correctness, minus the wall-clock pain.
🧭 Choose your usage style
| Use case | Best option | Boilerplate |
|---|---|---|
| Apply everywhere (most teams) | Global autouse (sleepfake_autouse = true or --sleepfake) |
Lowest |
| Per-test explicit control | sleepfake fixture |
Low |
| Decoration-style usage | @pytest.mark.sleepfake |
Low |
| Non-pytest scripts / direct control | SleepFake context manager |
Medium |
📚 Full details (expand as needed)
Context manager usage
import time
import asyncio
from sleepfake import SleepFake
# Sync
with SleepFake():
start = time.time()
time.sleep(10) # returns instantly
assert time.time() - start >= 10
# Async — use async with for proper cleanup of the background processor
async def test_async():
async with SleepFake():
start = asyncio.get_running_loop().time()
await asyncio.sleep(5) # returns instantly
assert asyncio.get_running_loop().time() - start >= 5
Customize freezegun ignores via ignore:
from sleepfake import SleepFake
# `_pytest.timing` is always ignored by default to keep pytest durations sane.
# Add your own modules as needed.
with SleepFake(ignore=["my_project.telemetry"]):
...
Fixture usage (`sleepfake`)
Install once; the sleepfake fixture is available automatically in tests.
import time
def test_retry_logic(sleepfake):
start = time.time()
time.sleep(30) # instantly skipped
assert time.time() - start >= 30
import asyncio
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; frozen clock sits at +5 s
assert asyncio.get_running_loop().time() - start >= 5
Deprecated:
asleepfakeis deprecated. Usesleepfakefor both sync and async tests.
Marker usage (`@pytest.mark.sleepfake`)
import time
import asyncio
import pytest
@pytest.mark.sleepfake
def test_marked_sync():
start = time.time()
time.sleep(100)
assert time.time() - start >= 100
@pytest.mark.sleepfake
async def test_marked_async():
start = asyncio.get_running_loop().time()
await asyncio.sleep(100)
assert asyncio.get_running_loop().time() - start >= 100
If a test already requests the sleepfake fixture, this marker becomes a no-op (no double patching).
Global autouse: all options and opt-out
Option A — config file (pyproject.toml / pytest.ini)
# pyproject.toml
[tool.pytest.ini_options]
sleepfake_autouse = true
sleepfake_ignore = ["my_project.telemetry", "my_project.metrics"]
# pytest.ini
[pytest]
sleepfake_autouse = true
sleepfake_ignore =
my_project.telemetry
my_project.metrics
Option B — CLI flag
pytest --sleepfake
pytest --sleepfake --sleepfake-ignore my_project.telemetry --sleepfake-ignore my_project.metrics
Disable autouse per-test
import time
import pytest
# This test runs with SleepFake (autouse applies).
def test_patched():
start = time.time()
time.sleep(100)
assert time.time() - start >= 100
# This test uses real time — SleepFake is NOT applied.
@pytest.mark.no_sleepfake
def test_needs_real_time():
start = time.time()
time.sleep(0.01)
assert time.time() - start < 5
@pytest.mark.no_sleepfake only disables the autouse layer.
If your test explicitly requests sleepfake, it still patches.
Option C — per-directory autouse in conftest.py
# conftest.py
import pytest
@pytest.fixture(autouse=True)
def _sleepfake_sync(sleepfake):
"""Auto-apply SleepFake for every test (sync and async)."""
@pytest.fixture(autouse=True)
async def _sleepfake_async(sleepfake):
"""Async counterpart — shares the same sleepfake instance; no double-patch."""
Both fixtures share one sleepfake instance.
Configure ignores in conftest.py
If you need project- or directory-specific ignore rules without touching pyproject.toml:
# conftest.py
pytest_sleepfake_ignore = ["my_project.telemetry", "my_project.metrics"]
This is used by:
- the
sleepfakefixture @pytest.mark.sleepfake- global autouse mode (
sleepfake_autouse = trueor--sleepfake)
asyncio.timeout integration
The frozen clock advances before each sleep future resolves, so asyncio.timeout still fires correctly:
import asyncio
import pytest
from sleepfake import SleepFake
async def test_timeout_fires():
with SleepFake():
with pytest.raises(TimeoutError):
async with asyncio.timeout(2):
await asyncio.sleep(10) # clock jumps to +10 s → timeout at +2 s fires
Options reference (API, CLI, config)
| Where | Option | Example | Purpose |
|---|---|---|---|
Python API (SleepFake) |
ignore: list[str] | None |
SleepFake(ignore=["my.module"]) |
Add module prefixes freezegun should ignore while freezing time. |
| Pytest CLI | --sleepfake |
pytest --sleepfake |
Enable SleepFake for every test in the session. |
| Pytest CLI | --sleepfake-ignore MODULE |
pytest --sleepfake-ignore my.module |
Add a module prefix to ignore (repeatable; merged with sleepfake_ignore). |
Pytest config (pytest.ini / pyproject.toml) |
sleepfake_autouse = true |
[tool.pytest.ini_options]\nsleepfake_autouse = true |
Same as --sleepfake, but persisted in config. |
Pytest config (pytest.ini / pyproject.toml) |
sleepfake_ignore |
sleepfake_ignore = ["my.module"] |
Add module prefixes to ignore for all pytest-managed SleepFake usage. |
conftest.py |
pytest_sleepfake_ignore |
pytest_sleepfake_ignore = ["my.module"] |
Override ignore prefixes for a test subtree (directory-scoped). |
Notes:
- Every ignore list is merged with
DEFAULT_IGNORE = ["_pytest.timing"]. This keeps pytest duration measurement on real clocks and prevents epoch-scale--durationsoutput. - User-provided ignore values are appended after
DEFAULT_IGNOREand deduplicated.
⚠️ Scope limitation
SleepFake patches time.sleep and asyncio.sleep by name via unittest.mock.patch.
Code that binds the function locally before the context is entered (for example from time import sleep at module import time) bypasses the patch.
🧪 How it works
| Aspect | Detail |
|---|---|
| Sync sleep | frozen_factory.tick(delta) advances frozen time immediately |
| Async sleep | (deadline, seq, future) goes into an asyncio.PriorityQueue; a background task resolves futures in deadline order |
| Timeout safety | After advancing time, the processor yields one event-loop turn so timeout callbacks can fire before futures resolve |
| Cancellation | Cancelled futures are skipped; the processor keeps running |
| pytest durations | freeze_time(..., ignore=["_pytest.timing", ...]) avoids breaking pytest internal wall-clock timing |
🤝 Contributing
PRs and issues welcome!
Note: SleepFake uses freezegun under the hood.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sleepfake-1.2.0.tar.gz.
File metadata
- Download URL: sleepfake-1.2.0.tar.gz
- Upload date:
- Size: 10.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
88e92bc36112cfe26e43428fe2f02885d223de55586dc2d61df6c85b17fc59e9
|
|
| MD5 |
4639d2f4266e1eb2289434a0e78a9e17
|
|
| BLAKE2b-256 |
829972f8c3093bdd8f99fa1bbd999645bb65912400a899f166d16acf5885be2a
|
Provenance
The following attestation bundles were made for sleepfake-1.2.0.tar.gz:
Publisher:
release.yml on Guiforge/sleepfake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sleepfake-1.2.0.tar.gz -
Subject digest:
88e92bc36112cfe26e43428fe2f02885d223de55586dc2d61df6c85b17fc59e9 - Sigstore transparency entry: 1191592597
- Sigstore integration time:
-
Permalink:
Guiforge/sleepfake@219af274c4dfd46af133f00303f0cc233653abe8 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/Guiforge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@219af274c4dfd46af133f00303f0cc233653abe8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sleepfake-1.2.0-py3-none-any.whl.
File metadata
- Download URL: sleepfake-1.2.0-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
724f2728917be95bc5f07ec2e2a10958de52d9e5627be6b8c85f90bbd48313c7
|
|
| MD5 |
abe2539d417caa5e5631abfb4c647614
|
|
| BLAKE2b-256 |
562085771972e7afdff93fee4db82709dad33eb9f362bae91d9b930410f93610
|
Provenance
The following attestation bundles were made for sleepfake-1.2.0-py3-none-any.whl:
Publisher:
release.yml on Guiforge/sleepfake
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sleepfake-1.2.0-py3-none-any.whl -
Subject digest:
724f2728917be95bc5f07ec2e2a10958de52d9e5627be6b8c85f90bbd48313c7 - Sigstore transparency entry: 1191592604
- Sigstore integration time:
-
Permalink:
Guiforge/sleepfake@219af274c4dfd46af133f00303f0cc233653abe8 -
Branch / Tag:
refs/tags/v1.2.0 - Owner: https://github.com/Guiforge
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@219af274c4dfd46af133f00303f0cc233653abe8 -
Trigger Event:
release
-
Statement type: