sain.sync

Synchronization primitives.

 1# BSD 3-Clause License
 2#
 3# Copyright (c) 2022-Present, nxtlo
 4# All rights reserved.
 5#
 6# Redistribution and use in source and binary forms, with or without
 7# modification, are permitted provided that the following conditions are met:
 8#
 9# * Redistributions of source code must retain the above copyright notice, this
10#   list of conditions and the following disclaimer.
11#
12# * Redistributions in binary form must reproduce the above copyright notice,
13#   this list of conditions and the following disclaimer in the documentation
14#   and/or other materials provided with the distribution.
15#
16# * Neither the name of the copyright holder nor the names of its
17#   contributors may be used to endorse or promote products derived from
18#   this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30"""Synchronization primitives."""
31
32from __future__ import annotations
33
34__all__ = ("Lazy", "Once", "LazyFuture", "AsyncOnce")
35
36from .lazy import Lazy
37from .lazy import LazyFuture
38from .once import AsyncOnce
39from .once import Once
@rustc_diagnostic_item('Lazy')
@typing.final
class Lazy(typing.Generic[~T]):
 49@rustc_diagnostic_item("Lazy")
 50@typing.final
 51class Lazy(typing.Generic[T]):
 52    """A thread-safe value that gets lazily initialized at first access.
 53
 54    This type is thread-safe and may be called from multiple threads since this value
 55    can be initialized from multiple threads, however, any calls to `Lazy.get` will block
 56    if another thread is initializing it.
 57
 58    Example
 59    -------
 60    ```py
 61    # some expensive call that returns a `str`
 62    def expensive_string() -> str:
 63        return "hehehe"
 64
 65    STRING: Lazy[str] = Lazy(expensive_string)
 66    print(STRING.get()) # The string is built, stored in the lazy lock and returned.
 67    print(STRING.get()) # The string is retrieved from the lazy lock.
 68    ```
 69    """
 70
 71    __slots__ = ("__inner", "__lock")
 72
 73    def __init__(self, f: collections.Callable[[], T]) -> None:
 74        self.__inner: T | collections.Callable[[], T] = f
 75        self.__lock: threading.Lock | None = None
 76
 77    def get(self) -> T:
 78        """Get the value if it was initialized, otherwise initialize it and return it.
 79
 80        Its guaranteed to not block if the value has been initialized.
 81
 82        Example
 83        -------
 84        ```py
 85        # some expensive call that returns a `str`
 86        def expensive_string() -> str:
 87            return "hehehe"
 88
 89        STRING: Lazy[str] = Lazy(expensive_string)
 90        print(STRING.get()) # The string is built, stored in the lazy lock and returned.
 91        print(STRING.get()) # The string is retrieved from the lazy lock.
 92        ```
 93        """
 94        if not callable(self.__inner):
 95            # value is already initialized, no need to make a call.
 96            return self.__inner
 97
 98        if self.__lock is None:
 99            self.__lock = threading.Lock()
100
101        with self.__lock:
102            # TYPE SAFETY: We know we need to call this function.
103            self.__inner = self.__inner()  # type: ignore
104            return self.__inner  # type: ignore
105
106    def __repr__(self) -> str:
107        if callable(self.__inner):
108            return "Lazy(uninit)"
109
110        return f"Lazy(value: {self.__inner.__repr__()})"
111
112    __str__ = __repr__
113
114    def __eq__(self, other: object) -> bool:
115        if not callable(self.__inner):
116            return False
117
118        if isinstance(other, Lazy):
119            return self.__inner == other.get()
120
121        return NotImplemented
122
123    def __ne__(self, other: object) -> bool:
124        return not self.__eq__(other)

A thread-safe value that gets lazily initialized at first access.

This type is thread-safe and may be called from multiple threads since this value can be initialized from multiple threads, however, any calls to Lazy.get will block if another thread is initializing it.

Example
# some expensive call that returns a `str`
def expensive_string() -> str:
    return "hehehe"

STRING: Lazy[str] = Lazy(expensive_string)
print(STRING.get()) # The string is built, stored in the lazy lock and returned.
print(STRING.get()) # The string is retrieved from the lazy lock.

Implementations

This class implements Lazy in Rust.

Lazy(f: Callable[[], ~T])
73    def __init__(self, f: collections.Callable[[], T]) -> None:
74        self.__inner: T | collections.Callable[[], T] = f
75        self.__lock: threading.Lock | None = None
def get(self) -> ~T:
 77    def get(self) -> T:
 78        """Get the value if it was initialized, otherwise initialize it and return it.
 79
 80        Its guaranteed to not block if the value has been initialized.
 81
 82        Example
 83        -------
 84        ```py
 85        # some expensive call that returns a `str`
 86        def expensive_string() -> str:
 87            return "hehehe"
 88
 89        STRING: Lazy[str] = Lazy(expensive_string)
 90        print(STRING.get()) # The string is built, stored in the lazy lock and returned.
 91        print(STRING.get()) # The string is retrieved from the lazy lock.
 92        ```
 93        """
 94        if not callable(self.__inner):
 95            # value is already initialized, no need to make a call.
 96            return self.__inner
 97
 98        if self.__lock is None:
 99            self.__lock = threading.Lock()
100
101        with self.__lock:
102            # TYPE SAFETY: We know we need to call this function.
103            self.__inner = self.__inner()  # type: ignore
104            return self.__inner  # type: ignore

Get the value if it was initialized, otherwise initialize it and return it.

Its guaranteed to not block if the value has been initialized.

Example
# some expensive call that returns a `str`
def expensive_string() -> str:
    return "hehehe"

STRING: Lazy[str] = Lazy(expensive_string)
print(STRING.get()) # The string is built, stored in the lazy lock and returned.
print(STRING.get()) # The string is retrieved from the lazy lock.
@macros.rustc_diagnostic_item('Once')
@typing.final
class Once(typing.Generic[~T]):
 54@macros.rustc_diagnostic_item("Once")
 55@typing.final
 56class Once(typing.Generic[T]):
 57    """A synchronization primitive which can be written to only once.
 58
 59    Example
 60    -------
 61    ```py
 62    from sain.once import Once
 63    from uuid import uuid4, UUID
 64
 65    UUID: Once[UUID] = Once()
 66
 67    def start() -> None:
 68        assert UUID.get().is_none()
 69
 70        # First initialization.
 71        UUID.get_or(uuid4()) # some-uuid
 72
 73        # Won't set, returns the same uuid that got initialized first.
 74        UUID.get_or(uuid4()) # some-uuid
 75    ```
 76    """
 77
 78    __slots__ = ("_inner", "_lock")
 79
 80    def __init__(self) -> None:
 81        self._lock: threading.Lock | None = None
 82        self._inner: T | None = None
 83
 84    @property
 85    def is_set(self) -> bool:
 86        return self._inner is not None
 87
 88    def get(self) -> Option[T]:
 89        """Gets the stored value, returning `None` if not initialized.
 90
 91        This method will never block.
 92        """
 93        return _option.Some(self._inner) if self.is_set else _option.NOTHING  # pyright: ignore
 94
 95    @macros.unsafe
 96    def get_unchecked(self) -> T:
 97        """Get the contained value without checking if it was initialized.
 98
 99        Example
100        -------
101        ```py
102        cell = Once[float]()
103        inner = cell.get_unchecked() # Undefined Behavior!!
104
105        # Initialize it first.
106        cell.get_or(math.sqrt(2.0))
107
108        # At this point of the program,
109        # it is guaranteed that the value is initialized.
110        inner = cell.get_unchecked()
111        ```
112        """
113        # SAFETY: The caller guarantees that the value is initialized.
114        return self.get().unwrap_unchecked()
115
116    def set(self, v: T) -> _result.Result[None, T]:
117        """Set the const value if its not set. returning `T` if its already set.
118
119        This method may block if another thread is trying to initialize the value.
120        The value is guaranteed to be set, just not necessarily the one provided.
121
122        Example
123        --------
124        ```py
125        flag = Once[bool]()
126        # flag is empty.
127        assert flag.get_or(True) is True.
128
129        # flag is not empty, so it returns the value we set first.
130        assert flag.set(False) == Err(True)
131        ```
132
133        Returns
134        -------
135        `sain.Result[None, T]`
136            This cell returns `Ok(None)` if it was empty. otherwise `Err(T)` if it was full.
137        """
138        if self._inner is not None:
139            return _result.Err(self._inner)
140
141        self._inner = self.get_or(v)
142        self._lock = None
143        return _result.Ok(None)
144
145    def clear(self) -> None:
146        """Clear the inner value, Setting it to `None`."""
147        self._lock = None
148        self._inner = None
149
150    def get_or(self, init: T) -> T:
151        """Get the value if it was not initialized, Otherwise set `init` value and returning it.
152
153        Many threads may call `get_or` concurrently with different
154        initializing functions, but it is guaranteed that only one function
155        will be executed.
156
157        Example
158        -------
159        ```py
160        UUID: Once[UUID] = Once()
161
162        def main() -> None:
163            assert UUID.get().is_none()
164
165            # First initialization.
166            UUID.get_or(uuid4()) # some-uuid
167
168            # Won't set, returns the same uuid that got initialized first.
169            UUID.get_or(uuid4()) # some-uuid
170            ```
171        """
172
173        # If the value is not empty we return it immediately.
174        if self._inner is not None:
175            return self._inner
176
177        if self._lock is None:
178            self._lock = threading.Lock()
179
180        with self._lock:
181            self._inner = init
182            return init
183
184    def get_or_with(self, f: collections.Callable[..., T]) -> T:
185        """Gets the contents of the cell, initializing it with `f` if the cell
186        was empty.
187
188        Many threads may call `get_or_with` concurrently with different
189        initializing functions, but it is guaranteed that only one function
190        will be executed.
191
192        Examples
193        --------
194        ```py
195        cell = Once[int]()
196        value = cell.get_or_with(lambda: 92)
197        assert value == 92
198
199        value = cell.get_or_with(lambda: 0)
200        assert value == 92
201        ```
202        """
203        # If the value is not empty we return it immediately.
204        if self._inner is not None:
205            return self._inner
206
207        if self._lock is None:
208            self._lock = threading.Lock()
209
210        with self._lock:
211            v = f()
212            self._inner = v
213            return v
214
215    def __repr__(self) -> str:
216        if not self.is_set:
217            return "<uninit>"
218
219        return f"Once(inner: {self._inner!r})"
220
221    __str__ = __repr__
222
223    def __eq__(self, __value: object) -> bool:
224        if isinstance(__value, Once):
225            return self._inner == __value._inner  # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
226
227        return NotImplemented
228
229    def __ne__(self, __value: object) -> bool:
230        return not self.__eq__(__value)
231
232    def __bool__(self) -> bool:
233        return self.is_set

A synchronization primitive which can be written to only once.

Example
from sain.once import Once
from uuid import uuid4, UUID

UUID: Once[UUID] = Once()

def start() -> None:
    assert UUID.get().is_none()

    # First initialization.
    UUID.get_or(uuid4()) # some-uuid

    # Won't set, returns the same uuid that got initialized first.
    UUID.get_or(uuid4()) # some-uuid

Implementations

This class implements Once in Rust.

is_set: bool
84    @property
85    def is_set(self) -> bool:
86        return self._inner is not None
def get(self) -> 'Option[T]':
88    def get(self) -> Option[T]:
89        """Gets the stored value, returning `None` if not initialized.
90
91        This method will never block.
92        """
93        return _option.Some(self._inner) if self.is_set else _option.NOTHING  # pyright: ignore

Gets the stored value, returning None if not initialized.

This method will never block.

@macros.unsafe
def get_unchecked(self) -> ~T:
 95    @macros.unsafe
 96    def get_unchecked(self) -> T:
 97        """Get the contained value without checking if it was initialized.
 98
 99        Example
100        -------
101        ```py
102        cell = Once[float]()
103        inner = cell.get_unchecked() # Undefined Behavior!!
104
105        # Initialize it first.
106        cell.get_or(math.sqrt(2.0))
107
108        # At this point of the program,
109        # it is guaranteed that the value is initialized.
110        inner = cell.get_unchecked()
111        ```
112        """
113        # SAFETY: The caller guarantees that the value is initialized.
114        return self.get().unwrap_unchecked()

Get the contained value without checking if it was initialized.

Example
cell = Once[float]()
inner = cell.get_unchecked() # Undefined Behavior!!

# Initialize it first.
cell.get_or(math.sqrt(2.0))

# At this point of the program,
# it is guaranteed that the value is initialized.
inner = cell.get_unchecked()

Safety ⚠️

Calling this method on None is considered undefined behavior.

def set(self, v: ~T) -> '_result.Result[None, T]':
116    def set(self, v: T) -> _result.Result[None, T]:
117        """Set the const value if its not set. returning `T` if its already set.
118
119        This method may block if another thread is trying to initialize the value.
120        The value is guaranteed to be set, just not necessarily the one provided.
121
122        Example
123        --------
124        ```py
125        flag = Once[bool]()
126        # flag is empty.
127        assert flag.get_or(True) is True.
128
129        # flag is not empty, so it returns the value we set first.
130        assert flag.set(False) == Err(True)
131        ```
132
133        Returns
134        -------
135        `sain.Result[None, T]`
136            This cell returns `Ok(None)` if it was empty. otherwise `Err(T)` if it was full.
137        """
138        if self._inner is not None:
139            return _result.Err(self._inner)
140
141        self._inner = self.get_or(v)
142        self._lock = None
143        return _result.Ok(None)

Set the const value if its not set. returning T if its already set.

This method may block if another thread is trying to initialize the value. The value is guaranteed to be set, just not necessarily the one provided.

Example
flag = Once[bool]()
# flag is empty.
assert flag.get_or(True) is True.

# flag is not empty, so it returns the value we set first.
assert flag.set(False) == Err(True)
Returns
  • sain.Result[None, T]: This cell returns Ok(None) if it was empty. otherwise Err(T) if it was full.
def clear(self) -> None:
145    def clear(self) -> None:
146        """Clear the inner value, Setting it to `None`."""
147        self._lock = None
148        self._inner = None

Clear the inner value, Setting it to None.

def get_or(self, init: ~T) -> ~T:
150    def get_or(self, init: T) -> T:
151        """Get the value if it was not initialized, Otherwise set `init` value and returning it.
152
153        Many threads may call `get_or` concurrently with different
154        initializing functions, but it is guaranteed that only one function
155        will be executed.
156
157        Example
158        -------
159        ```py
160        UUID: Once[UUID] = Once()
161
162        def main() -> None:
163            assert UUID.get().is_none()
164
165            # First initialization.
166            UUID.get_or(uuid4()) # some-uuid
167
168            # Won't set, returns the same uuid that got initialized first.
169            UUID.get_or(uuid4()) # some-uuid
170            ```
171        """
172
173        # If the value is not empty we return it immediately.
174        if self._inner is not None:
175            return self._inner
176
177        if self._lock is None:
178            self._lock = threading.Lock()
179
180        with self._lock:
181            self._inner = init
182            return init

Get the value if it was not initialized, Otherwise set init value and returning it.

Many threads may call get_or concurrently with different initializing functions, but it is guaranteed that only one function will be executed.

Example
UUID: Once[UUID] = Once()

def main() -> None:
    assert UUID.get().is_none()

    # First initialization.
    UUID.get_or(uuid4()) # some-uuid

    # Won't set, returns the same uuid that got initialized first.
    UUID.get_or(uuid4()) # some-uuid
   
def get_or_with(self, f: Callable[..., ~T]) -> ~T:
184    def get_or_with(self, f: collections.Callable[..., T]) -> T:
185        """Gets the contents of the cell, initializing it with `f` if the cell
186        was empty.
187
188        Many threads may call `get_or_with` concurrently with different
189        initializing functions, but it is guaranteed that only one function
190        will be executed.
191
192        Examples
193        --------
194        ```py
195        cell = Once[int]()
196        value = cell.get_or_with(lambda: 92)
197        assert value == 92
198
199        value = cell.get_or_with(lambda: 0)
200        assert value == 92
201        ```
202        """
203        # If the value is not empty we return it immediately.
204        if self._inner is not None:
205            return self._inner
206
207        if self._lock is None:
208            self._lock = threading.Lock()
209
210        with self._lock:
211            v = f()
212            self._inner = v
213            return v

Gets the contents of the cell, initializing it with f if the cell was empty.

Many threads may call get_or_with concurrently with different initializing functions, but it is guaranteed that only one function will be executed.

Examples
cell = Once[int]()
value = cell.get_or_with(lambda: 92)
assert value == 92

value = cell.get_or_with(lambda: 0)
assert value == 92
@typing.final
class LazyFuture(typing.Generic[~T]):
127@typing.final
128class LazyFuture(typing.Generic[T]):
129    """A thread-safe value that gets lazily initialized asynchronously at first access.
130
131    This type is thread-safe and may be called from multiple threads since this value
132    can be initialized from multiple threads, however, any calls to `Lazy.get` will block
133    if another thread is initializing it.
134
135    Example
136    -------
137    ```py
138    # some expensive call that returns a `str`
139    async def fetch_expensive_string(filtered: bool) -> str:
140        return "hehehe" if filtered else "whahaha"
141
142    STRING: LazyFuture[str] = LazyFuture(lambda: expensive_string(True))
143    print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
144    print(await STRING.get()) # The string is retrieved from the lazy lock.
145    ```
146    """
147
148    __slots__ = ("__inner", "__lock")
149
150    def __init__(
151        self,
152        f: collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]],
153    ) -> None:
154        self.__inner: (
155            T
156            | collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]]
157        ) = f
158        self.__lock: asyncio.Lock | None = asyncio.Lock()
159
160    async def get(self) -> T:
161        """Get the value if it was initialized, otherwise initialize it and return it.
162
163        Example
164        -------
165        ```py
166        # some expensive call that returns a `str`
167        async def fetch_expensive_string(filtered: bool) -> str:
168            return "hehehe" if filtered else "whahaha"
169
170        STRING: LazyFuture[str] = LazyFuture(lambda: expensive_string(True))
171        print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
172        print(await STRING.get()) # The string is retrieved from the lazy lock.
173        ```
174        """
175        if not callable(self.__inner):
176            # value is already initialized, no need to make a call.
177            return self.__inner
178
179        if self.__lock is None:
180            self.__lock = asyncio.Lock()
181
182        async with self.__lock:
183            # calling self.__inner will make self.__inner type T
184            v = await self.__inner()  # pyright: ignore
185            self.__inner = v
186            return v
187
188    def __repr__(self) -> str:
189        if not callable(self.__inner):
190            return "LazyFuture(uninit)"
191
192        return f"LazyFuture(value: {self.__inner!r})"
193
194    __str__ = __repr__
195
196    def __eq__(self, other: object) -> bool:
197        if not callable(self.__inner):
198            return False
199
200        if isinstance(other, Lazy):
201            return self._inner == other._inner  # pyright: ignore
202
203        return NotImplemented
204
205    def __ne__(self, other: object) -> bool:
206        return not self.__eq__(other)

A thread-safe value that gets lazily initialized asynchronously at first access.

This type is thread-safe and may be called from multiple threads since this value can be initialized from multiple threads, however, any calls to Lazy.get will block if another thread is initializing it.

Example
# some expensive call that returns a `str`
async def fetch_expensive_string(filtered: bool) -> str:
    return "hehehe" if filtered else "whahaha"

STRING: LazyFuture[str] = LazyFuture(lambda: expensive_string(True))
print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
print(await STRING.get()) # The string is retrieved from the lazy lock.
LazyFuture(f: Callable[[], Coroutine[typing.Any, typing.Any, ~T]])
150    def __init__(
151        self,
152        f: collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]],
153    ) -> None:
154        self.__inner: (
155            T
156            | collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]]
157        ) = f
158        self.__lock: asyncio.Lock | None = asyncio.Lock()
async def get(self) -> ~T:
160    async def get(self) -> T:
161        """Get the value if it was initialized, otherwise initialize it and return it.
162
163        Example
164        -------
165        ```py
166        # some expensive call that returns a `str`
167        async def fetch_expensive_string(filtered: bool) -> str:
168            return "hehehe" if filtered else "whahaha"
169
170        STRING: LazyFuture[str] = LazyFuture(lambda: expensive_string(True))
171        print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
172        print(await STRING.get()) # The string is retrieved from the lazy lock.
173        ```
174        """
175        if not callable(self.__inner):
176            # value is already initialized, no need to make a call.
177            return self.__inner
178
179        if self.__lock is None:
180            self.__lock = asyncio.Lock()
181
182        async with self.__lock:
183            # calling self.__inner will make self.__inner type T
184            v = await self.__inner()  # pyright: ignore
185            self.__inner = v
186            return v

Get the value if it was initialized, otherwise initialize it and return it.

Example
# some expensive call that returns a `str`
async def fetch_expensive_string(filtered: bool) -> str:
    return "hehehe" if filtered else "whahaha"

STRING: LazyFuture[str] = LazyFuture(lambda: expensive_string(True))
print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
print(await STRING.get()) # The string is retrieved from the lazy lock.
@typing.final
class AsyncOnce(typing.Generic[~T]):
236@typing.final
237class AsyncOnce(typing.Generic[T]):
238    """A synchronization primitive which can be written to only once.
239
240    This is an `async` version of `Once`.
241
242    Example
243    -------
244    ```py
245    from sain.once import Once
246    from uuid import uuid4, UUID
247
248    # A global uuid
249    UUID: AsyncOnce[UUID] = AsyncOnce()
250
251    async def start() -> None:
252        assert UUID.get().is_none()
253        # First initialization.
254        await UUID.get_or(uuid4()) # some-uuid
255        # Won't set, returns the same uuid that got initialized first.
256        await UUID.get_or(uuid4()) # some-uuid
257    ```
258    """
259
260    __slots__ = ("_inner", "_lock")
261
262    def __init__(self) -> None:
263        self._lock: asyncio.Lock | None = None
264        self._inner: T | None = None
265
266    @property
267    def is_set(self) -> bool:
268        """Whether this inner value has ben initialized or not."""
269        return self._inner is not None
270
271    def get(self) -> Option[T]:
272        """Gets the stored value. `Some(None)` is returned if nothing is stored.
273
274        This method will never block.
275        """
276        return _option.Some(self._inner) if self.is_set else _option.NOTHING  # pyright: ignore
277
278    @macros.unsafe
279    def get_unchecked(self) -> T:
280        """Get the contained value without checking if it was initialized.
281
282        Example
283        -------
284        ```py
285        cell = AsyncOnce[float]()
286        inner = cell.get_unchecked() # Undefined Behavior!!
287
288        # Initialize it first.
289        cell.get_or(math.sqrt(2.0))
290
291        # At this point of the program,
292        # it is guaranteed that the value is initialized.
293        inner = cell.get_unchecked()
294        ```
295        """
296        # SAFETY: The caller guarantees that the value is initialized.
297        return self.get().unwrap_unchecked()
298
299    async def set(self, v: T) -> _result.Result[None, T]:
300        """Set the const value if its not set. returning `T` if its already set.
301
302        if another thread is trying to initialize the value, The value is guaranteed to be set,
303        just not necessarily the one provided.
304
305        Example
306        --------
307        ```py
308        flag = AsyncOnce[bool]()
309        # flag is empty.
310        assert await flag.get_or(True) is True.
311        # flag is not empty, so it returns the value we set first.
312        assert await flag.set(False) == Err(True)
313        ```
314
315        Returns
316        -------
317        `sain.Result[None, T]`
318            This cell returns `Ok(None)` if it was empty. otherwise `Err(T)` if it was full.
319        """
320        if self._inner is not None:
321            return _result.Err(self._inner)
322
323        self._inner = await self.get_or(v)
324        self._lock = None
325        return _result.Ok(None)
326
327    def clear(self) -> None:
328        """Clear the inner value, Setting it to `None`."""
329        self._lock = None
330        self._inner = None
331
332    async def get_or(self, init: T) -> T:
333        """Gets the contents of the cell, initializing it with `init` if the cell
334        was empty.
335
336        Many threads may call `get_or` concurrently with different
337        initializing functions, but it is guaranteed that only one function
338        will be executed.
339
340        Examples
341        --------
342        ```py
343        from sain.sync import AsyncOnce
344
345        cell = AsyncOnce[int]()
346        value = await cell.get_or(92)
347        assert value == 92
348
349        value = await cell.get_or(0)
350        assert value == 92
351        ```
352        """
353        # If the value is not empty we return it immediately.
354        if self._inner is not None:
355            return self._inner
356
357        if self._lock is None:
358            self._lock = asyncio.Lock()
359
360        async with self._lock:
361            self._inner = init
362            return init
363
364    async def get_or_with(self, f: collections.Callable[..., T]) -> T:
365        """Gets the contents of the cell, initializing it with `f` if the cell
366        was empty.
367
368        Many threads may call `get_or_with` concurrently with different
369        initializing functions, but it is guaranteed that only one function
370        will be executed.
371
372        Examples
373        --------
374        ```py
375        from sain.sync import AsyncOnce
376
377        cell = AsyncOnce[int]()
378        value = await cell.get_or_with(lambda: 92)
379        assert value == 92
380
381        value = await cell.get_or_init(lambda: 0)
382        assert value == 92
383        ```
384        """
385        # If the value is not empty we return it immediately.
386        if self._inner is not None:
387            return self._inner
388
389        if self._lock is None:
390            self._lock = asyncio.Lock()
391
392        async with self._lock:
393            v = f()
394            self._inner = v
395            return v
396
397    def __repr__(self) -> str:
398        if self._inner is not None:
399            return f"AsyncOnce(value: {self._inner})"
400        return "<async_uninit>"
401
402    __str__ = __repr__
403
404    def __eq__(self, __value: object) -> bool:
405        if isinstance(__value, AsyncOnce):
406            return self._inner == __value._inner  # pyright: ignore
407
408        return NotImplemented
409
410    def __ne__(self, __value: object) -> bool:
411        return not self.__eq__(__value)
412
413    def __bool__(self) -> bool:
414        return self.is_set

A synchronization primitive which can be written to only once.

This is an async version of Once.

Example
from sain.once import Once
from uuid import uuid4, UUID

# A global uuid
UUID: AsyncOnce[UUID] = AsyncOnce()

async def start() -> None:
    assert UUID.get().is_none()
    # First initialization.
    await UUID.get_or(uuid4()) # some-uuid
    # Won't set, returns the same uuid that got initialized first.
    await UUID.get_or(uuid4()) # some-uuid
is_set: bool
266    @property
267    def is_set(self) -> bool:
268        """Whether this inner value has ben initialized or not."""
269        return self._inner is not None

Whether this inner value has ben initialized or not.

def get(self) -> 'Option[T]':
271    def get(self) -> Option[T]:
272        """Gets the stored value. `Some(None)` is returned if nothing is stored.
273
274        This method will never block.
275        """
276        return _option.Some(self._inner) if self.is_set else _option.NOTHING  # pyright: ignore

Gets the stored value. Some(None) is returned if nothing is stored.

This method will never block.

@macros.unsafe
def get_unchecked(self) -> ~T:
278    @macros.unsafe
279    def get_unchecked(self) -> T:
280        """Get the contained value without checking if it was initialized.
281
282        Example
283        -------
284        ```py
285        cell = AsyncOnce[float]()
286        inner = cell.get_unchecked() # Undefined Behavior!!
287
288        # Initialize it first.
289        cell.get_or(math.sqrt(2.0))
290
291        # At this point of the program,
292        # it is guaranteed that the value is initialized.
293        inner = cell.get_unchecked()
294        ```
295        """
296        # SAFETY: The caller guarantees that the value is initialized.
297        return self.get().unwrap_unchecked()

Get the contained value without checking if it was initialized.

Example
cell = AsyncOnce[float]()
inner = cell.get_unchecked() # Undefined Behavior!!

# Initialize it first.
cell.get_or(math.sqrt(2.0))

# At this point of the program,
# it is guaranteed that the value is initialized.
inner = cell.get_unchecked()

Safety ⚠️

Calling this method on None is considered undefined behavior.

async def set(self, v: ~T) -> '_result.Result[None, T]':
299    async def set(self, v: T) -> _result.Result[None, T]:
300        """Set the const value if its not set. returning `T` if its already set.
301
302        if another thread is trying to initialize the value, The value is guaranteed to be set,
303        just not necessarily the one provided.
304
305        Example
306        --------
307        ```py
308        flag = AsyncOnce[bool]()
309        # flag is empty.
310        assert await flag.get_or(True) is True.
311        # flag is not empty, so it returns the value we set first.
312        assert await flag.set(False) == Err(True)
313        ```
314
315        Returns
316        -------
317        `sain.Result[None, T]`
318            This cell returns `Ok(None)` if it was empty. otherwise `Err(T)` if it was full.
319        """
320        if self._inner is not None:
321            return _result.Err(self._inner)
322
323        self._inner = await self.get_or(v)
324        self._lock = None
325        return _result.Ok(None)

Set the const value if its not set. returning T if its already set.

if another thread is trying to initialize the value, The value is guaranteed to be set, just not necessarily the one provided.

Example
flag = AsyncOnce[bool]()
# flag is empty.
assert await flag.get_or(True) is True.
# flag is not empty, so it returns the value we set first.
assert await flag.set(False) == Err(True)
Returns
  • sain.Result[None, T]: This cell returns Ok(None) if it was empty. otherwise Err(T) if it was full.
def clear(self) -> None:
327    def clear(self) -> None:
328        """Clear the inner value, Setting it to `None`."""
329        self._lock = None
330        self._inner = None

Clear the inner value, Setting it to None.

async def get_or(self, init: ~T) -> ~T:
332    async def get_or(self, init: T) -> T:
333        """Gets the contents of the cell, initializing it with `init` if the cell
334        was empty.
335
336        Many threads may call `get_or` concurrently with different
337        initializing functions, but it is guaranteed that only one function
338        will be executed.
339
340        Examples
341        --------
342        ```py
343        from sain.sync import AsyncOnce
344
345        cell = AsyncOnce[int]()
346        value = await cell.get_or(92)
347        assert value == 92
348
349        value = await cell.get_or(0)
350        assert value == 92
351        ```
352        """
353        # If the value is not empty we return it immediately.
354        if self._inner is not None:
355            return self._inner
356
357        if self._lock is None:
358            self._lock = asyncio.Lock()
359
360        async with self._lock:
361            self._inner = init
362            return init

Gets the contents of the cell, initializing it with init if the cell was empty.

Many threads may call get_or concurrently with different initializing functions, but it is guaranteed that only one function will be executed.

Examples
from sain.sync import AsyncOnce

cell = AsyncOnce[int]()
value = await cell.get_or(92)
assert value == 92

value = await cell.get_or(0)
assert value == 92
async def get_or_with(self, f: Callable[..., ~T]) -> ~T:
364    async def get_or_with(self, f: collections.Callable[..., T]) -> T:
365        """Gets the contents of the cell, initializing it with `f` if the cell
366        was empty.
367
368        Many threads may call `get_or_with` concurrently with different
369        initializing functions, but it is guaranteed that only one function
370        will be executed.
371
372        Examples
373        --------
374        ```py
375        from sain.sync import AsyncOnce
376
377        cell = AsyncOnce[int]()
378        value = await cell.get_or_with(lambda: 92)
379        assert value == 92
380
381        value = await cell.get_or_init(lambda: 0)
382        assert value == 92
383        ```
384        """
385        # If the value is not empty we return it immediately.
386        if self._inner is not None:
387            return self._inner
388
389        if self._lock is None:
390            self._lock = asyncio.Lock()
391
392        async with self._lock:
393            v = f()
394            self._inner = v
395            return v

Gets the contents of the cell, initializing it with f if the cell was empty.

Many threads may call get_or_with concurrently with different initializing functions, but it is guaranteed that only one function will be executed.

Examples
from sain.sync import AsyncOnce

cell = AsyncOnce[int]()
value = await cell.get_or_with(lambda: 92)
assert value == 92

value = await cell.get_or_init(lambda: 0)
assert value == 92