Skip to main content

Ensure a function runs only once, regardless of how many times it's called.

Project description

philiprehberger-once

Tests PyPI version Last updated

Ensure a function runs only once, regardless of how many times it's called.

Installation

pip install philiprehberger-once

Usage

from philiprehberger_once import once

@once
def load_config():
    print("Loading...")
    return {"debug": True}

load_config()  # prints "Loading...", returns {"debug": True}
load_config()  # returns {"debug": True} without printing

Async support

import asyncio
from philiprehberger_once import once

@once
async def fetch_token():
    print("Fetching...")
    return "abc-123"

asyncio.run(fetch_token())  # prints "Fetching...", returns "abc-123"
asyncio.run(fetch_token())  # returns "abc-123" without fetching

Once per key

from philiprehberger_once import once_per_key

@once_per_key
def connect(host, port=5432):
    print(f"Connecting to {host}...")
    return f"conn:{host}"

connect("db-1")  # prints "Connecting to db-1...", returns "conn:db-1"
connect("db-1")  # returns cached "conn:db-1"
connect("db-2")  # prints "Connecting to db-2...", returns "conn:db-2"

Once per key (async)

import asyncio
from philiprehberger_once import once_per_key_async

@once_per_key_async
async def fetch_user(user_id: str):
    print(f"Fetching {user_id}...")
    return {"id": user_id}

async def main():
    # Concurrent awaiters of the same key share one in-flight execution.
    a, b, c = await asyncio.gather(
        fetch_user("u1"),
        fetch_user("u1"),
        fetch_user("u2"),
    )
    # Only two prints: one for "u1" and one for "u2".

asyncio.run(main())

# Derive the cache key from a callable instead of the first arg:
@once_per_key_async(key=lambda req, **kw: kw["uid"])
async def load(req, *, uid: str):
    return uid

Once per args

Use the entire call signature (positional + keyword) as the cache key — handy when the value depends on more than just the first argument.

from philiprehberger_once import once_per_args

@once_per_args
def init(host: str, port: int):
    print(f"connecting to {host}:{port}")
    return f"{host}:{port}"

init("a", 1)   # prints, returns "a:1"
init("a", 1)   # cached
init("a", 2)   # prints, returns "a:2"

Reset and inspect

from philiprehberger_once import once

@once
def init():
    return 42

init()
init.called   # True
init.reset()
init.called   # False
init()        # runs again

API

Function / Property Description
once(fn) Decorator. Runs fn once, caches and returns the result on subsequent calls. Thread-safe. Supports async.
once_per_key(fn) Decorator. Runs fn once per unique first argument. Thread-safe.
once_per_args(fn) Decorator. Runs fn once per unique combination of positional and keyword arguments. Thread-safe. All arguments must be hashable.
once_per_key_async(fn=None, *, key=None) Decorator for async functions. Runs the coroutine once per unique key (first positional arg, or derived via key=...). Concurrent awaiters of the same key share one in-flight execution.
.called bool for once, dict[key, bool] for once_per_key and once_per_key_async. Whether the function has been called.
.reset() Clear cached result so the function can run again. once_per_key and once_per_key_async accept an optional key argument.

Development

pip install -e .
python -m pytest tests/ -v

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

philiprehberger_once-0.3.0.tar.gz (8.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

philiprehberger_once-0.3.0-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file philiprehberger_once-0.3.0.tar.gz.

File metadata

  • Download URL: philiprehberger_once-0.3.0.tar.gz
  • Upload date:
  • Size: 8.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for philiprehberger_once-0.3.0.tar.gz
Algorithm Hash digest
SHA256 148c8ebb6afac0a6a44f74e662150121020c03cfa4027ba8d8da3546661ebaa8
MD5 e4434b5ef40ba035e0d1197bb7c5253c
BLAKE2b-256 1f2e306550ce6c34d32e91cf2cafec4c8c2f8e37d4b00595b7c267f6e3df8dca

See more details on using hashes here.

File details

Details for the file philiprehberger_once-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for philiprehberger_once-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ecfbc70b73d382a5f9073aa704b37c716e6f01a12843d62cc2013eebd5f1ca6c
MD5 f0eea545cbf44f9e27e644abd1b20d25
BLAKE2b-256 d631a98b2d175f0fef0ca1fa95616018f3c0412b64edee8b5d384d9a730ac94f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page