Skip to main content

calltimeout

Lianes8 Certified

Run a callable with a wall-clock timeout and get a typed result or a clean TimeoutError, with an honest choice of mechanism.

A call that hangs (a network read without a timeout, a runaway loop, a stuck library) takes its caller down with it. Putting a time limit on it looks easy and is not: a thread cannot be stopped, a process needs everything to pickle, and a signal only works in the main thread on POSIX. calltimeout offers all three behind one typed function and says plainly what each one can and cannot do.

Install

pip install calltimeout

Python 3.10 or newer, no dependencies.

Usage

import time

from calltimeout import CallTimeout, run, with_timeout


def fetch(delay: float) -> str:
    time.sleep(delay)
    return "payload"


# The result, when the call finishes in time.
print(run(fetch, 2.0, args=(0.01,)))  # payload

# CallTimeout, a subclass of TimeoutError, when it does not.
try:
    run(fetch, 0.05, args=(1.0,))
except CallTimeout as exc:
    print(f"gave up after {exc.timeout} seconds")  # gave up after 0.05 seconds

# Or a fallback instead of raising.
print(run(fetch, 0.05, args=(1.0,), default="cached"))  # cached


# The same as a decorator that keeps the function's signature for type checkers.
@with_timeout(2.0)
def add(left: int, right: int) -> int:
    return left + right


print(add(2, 3))  # 5

Choosing a mechanism

The trade-off is the whole point of the library, so it is stated plainly:

"thread" (default) "process" "signal"
Stops a runaway call No: it keeps running in a daemon thread Yes: terminated, and killed if still alive 1 second later Python code and blocking system calls: yes. A long call inside C: once it returns
Where it works Everywhere Everywhere POSIX, main thread only
Pickling Not needed The result always; fn and its arguments under spawn and forkserver Not needed
Cost A thread A new process, whose start and the transfer of its result count against the budget A timer
Context variables A copy of the caller's, like asyncio.to_thread A copy under fork, the defaults under spawn and forkserver; changes never come back Shared: same thread
import math
import time

from calltimeout import run


def main() -> None:
    # Process mode really stops the work: the child is terminated, not left running.
    print(run(time.sleep, 0.5, args=(60,), mode="process", default="stopped"))  # stopped
    print(run(math.factorial, 30.0, args=(20,), mode="process"))  # 2432902008176640000


if __name__ == "__main__":
    main()

Process mode needs importable targets and, in a script, the if __name__ == "__main__": guard that multiprocessing asks for, as above: under the spawn and forkserver start methods the child imports the script again. The GUIDE explains each mode in depth, every error and its cause, nesting, and use from async code.

API

Name What it is
run(fn, timeout, *, args=(), kwargs=None, mode="thread", default=...) Call fn(*args, **kwargs) with a budget of timeout seconds and return its result
with_timeout(timeout, *, mode="thread", default=...) The same as a decorator; the decorated function keeps its parameters
CallTimeout A TimeoutError raised when the budget runs out; .timeout is the budget
CallCrashed A RuntimeError raised in process mode when the child dies without a result; .exitcode
RemoteTraceback The cause chained to an exception from a process-mode call; its text (.text) is the child's traceback
Mode Literal["thread", "process", "signal"], for annotating your own wrappers

Bad arguments raise TypeError or ValueError before anything starts. In process mode under the spawn and forkserver start methods, a call that cannot be pickled raises pickle.PicklingError before the call runs (fork pickles only the result). The CHANGELOG lists what changed in each release; from 1.0.0 on the API follows Semantic Versioning.

Why

Checked on PyPI on 2026-10-22:

  • pebble (5.2.3, maintained and typed) runs calls in threads, processes and pools, and reports a crashed process with its exit code. Its decorators return a Future instead of the result, and its thread decorator takes no timeout, since a thread cannot be stopped. It is the better choice for pools of work.
  • wrapt-timeout-decorator (1.5.1, February 2024, typed) uses signals by default and can run the call in a subprocess instead; it installs six runtime dependencies (wrapt, multiprocess, dill, psutil and two helpers).
  • func-timeout (last release 2019), stopit (2018) and timeout-decorator (2020) ship no type information and have not had a release in years.

calltimeout does one thing: a blocking call with a deadline that returns the plain, typed result. It depends on nothing, is checked with mypy --strict, ships py.typed, keeps the decorated function's signature (ParamSpec), and makes the choice between thread, process and signal explicit, with the limits of each written down and tested.

Development

pip install -r requirements-dev.txt, then scripts/check.ps1 runs format, lint, types and tests. python scripts/fetch_upstream.py downloads the CPython documentation pages listed in tests/upstream/MANIFEST.json, which some tests read (they skip without them), and pip install pebble adds a second implementation the process tests compare against. Set CALLTIMEOUT_E2E=1 to also build the wheel and sdist and test the installed package, and CALLTIMEOUT_START_METHOD to run under spawn, fork or forkserver.

License

MIT. See LICENSE.

Metadata

Release files for calltimeout 1.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 calltimeout 1.0.0
File Size Uploaded Provenance Actions
calltimeout-1.0.0.tar.gz 96.0 kB Without provenance Details for calltimeout-1.0.0.tar.gz

Built distribution (wheel)

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

Total release size: 127.2 kB

Release files / calltimeout-1.0.0.tar.gz

Download URL calltimeout-1.0.0.tar.gz
Size 96.0 kB
Tags Source
SHA-256 checksum
How to use checksums
de807b70d67fb94c3f5f464ea6b998d493c0b2f19400ae9a9b21ddd737ba47ad
BLAKE2b-256 checksum
How to use checksums
828d3de0ccda60a92d5c278525264392090361f1e164c49e62892fff967ecd1f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Release files / calltimeout-1.0.0-py3-none-any.whl

Download URL calltimeout-1.0.0-py3-none-any.whl
Size 31.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
82120e7973a1978497d78ea451f92dfbf8d72aa43697891e7d9a0d23720fa3e5
BLAKE2b-256 checksum
How to use checksums
c5b9c5dfe8a575e66b997f1dfaee41e7395ab41663500c09ea5398c56fcec92e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.2

Security for calltimeout 1.0.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

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