Skip to main content

Once

This library provides functionality to ensure a function is called exactly once in Python, heavily inspired by std::call_once.

During initialization, we often want to ensure code is run exactly once. But thinking about all the different ways this constraint can be violated can be time-consuming and complex. We don't want to have to reason about what other callers are doing and from which thread.

Introducing a simple solution - the once.once decorator! Simply decorate a function with this decorator, and this library will handle all the edge cases to ensure it is called exactly once! The first call will invoke the function, and all subsequent calls will return the same result. It works on both sync and aysnc functions, and methods. Enough talking, let's cut to an example:

import once

@once.once
def my_expensive_object():
    load_expensive_resource()
    load_more_expensive_resources()
    return ObjectSingletonUsingLotsOfMemory()

def caller_one():
    my_expensive_object().use_it()

def caller_two_from_a_separate_thread():
    my_expensive_object().use_it()

def optional_init_function_to_prewarm():
    my_expensive_object()

@once.once
async def slow_expensive_object():
    await load_expensive_async_resource()

@once.once(per_thread=True)
async def slow_expensive_non_threadsafe_object():
    await load_expensive_async_resource()

async def user_slow_expensive_object():
    await slow_expensive_object()

This module is extremely simple, with no external dependencies, and heavily tested for races.

Use with methods

There are two versions of the decorator for methods, once_per_class and once_per_instance. The once_per_class decorator calls the function only only once for the defined class, and the once_per_instance decorator once for each separate object instance created from the class.

class MyClass:

    @once.once_per_class
    def fn1(self):
        pass

    @once.once_per_instance
    @classmethod
    async def fn2(cls):
        pass

A = MyClass()
B = MyClass()

A.fn1()
B.fn1()

A.fn2()  # cached
B.fn2()  # calls again
B.fn2()  # cached

Options

The behavior of the decorator is configurable with boolean options, which default to False. For the function decorator, options can be specified by simply passing them into the decorator:

@once.once(per_thread=True)
def non_thread_safe_constructor():
    pass

For methods, pass options into the with_options modifier

class MyClass:
    @once.once_per_class.with_options(per_thread=True)
    def non_thread_safe_constructor(self):
        pass

per_thread

This instantiates the function once per thread, and will return a thread-local result for each separate thread. This is extremely convenient for expensive objects which are not thread-safe. Under the hood, this uses threading.local for its caching.

allow_reset

This exposes a reset method on the function, which will force the underlying function to be called again. The reset acts at the same scope as the underlying caching behavior, so a per_thread call will only act on that thread.

@once.once(allow_reset=True)
def resettable_fn():
    pass

resetable_fn()
resetable_fn.reset()
resetable_fn()  # calls again

retry_exceptions

This will invoke the underlying function again if it raises an unhandled exception.

Metadata

Release files for once-py 0.0.8

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

Built distribution (wheel)

Table of built distributions (wheels) for once-py 0.0.8
File Interpreter ABI Platform Actions
once_py-0.0.8-py3-none-any.whl Python 3 none any Details for once_py-0.0.8-py3-none-any.whl

Release files / once_py-0.0.8-py3-none-any.whl

Download URL once_py-0.0.8-py3-none-any.whl
Size 11.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4eeaa3fc6defb95ba0efff884b8f758911257abcc2d52d906a8bde0080e935f3
BLAKE2b-256 checksum
How to use checksums
2f8be81ffe43f650e0d13a5f8355fca462aee59d5dad64d98280cd48fef5be28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Dec 2, 2025.

Transparency log

Security for once-py 0.0.8

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.

Full provenance

1 file in this release has been uploaded with provenance metadata.

Attestation for once_py-0.0.8-py3-none-any.whl

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 Dec 2, 2025.

Transparency log

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.

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