Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add details about options to documentation. #26

Merged
merged 1 commit into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 82 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ 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. Enough talking, let's
cut to an example:
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:

```python
import once
Expand All @@ -32,7 +32,87 @@ def caller_two_from_a_separate_thread():
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)
aebrahim marked this conversation as resolved.
Show resolved Hide resolved
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
aebrahim marked this conversation as resolved.
Show resolved Hide resolved
`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.
```python
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:
```python
@once.once(per_thread=True)
def non_thread_safe_constructor():
pass
```

For methods, pass options into the `with_options` modifier
```python

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.

### `allow_reset`
This exposes a `reset` method on the function, which will force the underlying
aebrahim marked this conversation as resolved.
Show resolved Hide resolved
function to be called again.
aebrahim marked this conversation as resolved.
Show resolved Hide resolved
```python
@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.
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ classifiers = [
]

[project.urls]
"Homepage" = "https://github.com/DelfinaCare/once"
"Bug Tracker" = "https://github.com/DelfinaCare/once/issues"
Homepage = "https://github.com/DelfinaCare/once"
Issues = "https://github.com/DelfinaCare/once/issues"
Repository = "https://github.com/DelfinaCare/once.git"
Loading