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        return f"Lazy(value: {self.__inner!r})"
108
109    __str__ = __repr__

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
 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 __bool__(self) -> bool:
224        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

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 without knowing the output 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]):
112@typing.final
113class LazyFuture(typing.Generic[T]):
114    """A thread-safe value that gets lazily initialized asynchronously at first access.
115
116    This type is thread-safe and may be called from multiple threads since this value
117    can be initialized from multiple threads, however, any calls to `Lazy.get` will block
118    if another thread is initializing it.
119
120    Example
121    -------
122    ```py
123    # some expensive call that returns a `str`
124    async def fetch_expensive_string(filtered: bool) -> str:
125        return "hehehe" if filtered else "whahaha"
126
127    STRING: LazyFuture[str] = LazyFuture(lambda: expensive_string(True))
128    print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
129    print(await STRING.get()) # The string is retrieved from the lazy lock.
130    ```
131    """
132
133    __slots__ = ("__inner", "__lock")
134
135    def __init__(
136        self,
137        f: collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]],
138    ) -> None:
139        self.__inner: (
140            T
141            | collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]]
142        ) = f
143        self.__lock: asyncio.Lock | None = asyncio.Lock()
144
145    async def get(self) -> T:
146        """Get the value if it was initialized, otherwise initialize it and return it.
147
148        Example
149        -------
150        ```py
151        # some expensive call that returns a `str`
152        async def fetch_expensive_string(filtered: bool) -> str:
153            return "hehehe" if filtered else "whahaha"
154
155        STRING: LazyFuture[str] = LazyFuture(lambda: fetch_expensive_string(True))
156        print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
157        print(await STRING.get()) # The string is retrieved from the lazy lock.
158        ```
159        """
160        if not callable(self.__inner):
161            # value is already initialized, no need to make a call.
162            return self.__inner
163
164        if self.__lock is None:
165            self.__lock = asyncio.Lock()
166
167        async with self.__lock:
168            # calling self.__inner will make self.__inner type T
169            v = await self.__inner()  # pyright: ignore
170            self.__inner = v
171            return v  # pyright: ignore
172
173    def __repr__(self) -> str:
174        return f"LazyFuture(value: {self.__inner!r})"
175
176    __str__ = __repr__

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]])
135    def __init__(
136        self,
137        f: collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]],
138    ) -> None:
139        self.__inner: (
140            T
141            | collections.Callable[[], collections.Coroutine[typing.Any, typing.Any, T]]
142        ) = f
143        self.__lock: asyncio.Lock | None = asyncio.Lock()
async def get(self) -> ~T:
145    async def get(self) -> T:
146        """Get the value if it was initialized, otherwise initialize it and return it.
147
148        Example
149        -------
150        ```py
151        # some expensive call that returns a `str`
152        async def fetch_expensive_string(filtered: bool) -> str:
153            return "hehehe" if filtered else "whahaha"
154
155        STRING: LazyFuture[str] = LazyFuture(lambda: fetch_expensive_string(True))
156        print(await STRING.get()) # The string is built, stored in the lazy lock and returned.
157        print(await STRING.get()) # The string is retrieved from the lazy lock.
158        ```
159        """
160        if not callable(self.__inner):
161            # value is already initialized, no need to make a call.
162            return self.__inner
163
164        if self.__lock is None:
165            self.__lock = asyncio.Lock()
166
167        async with self.__lock:
168            # calling self.__inner will make self.__inner type T
169            v = await self.__inner()  # pyright: ignore
170            self.__inner = v
171            return v  # pyright: ignore

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

Whether this inner value has ben initialized or not.

def get(self) -> 'Option[T]':
262    def get(self) -> Option[T]:
263        """Gets the stored value. `Some(None)` is returned if nothing is stored.
264
265        This method will never block.
266        """
267        return _option.Some(self._inner) if self.is_set else _option.NOTHING

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

This method will never block.

@macros.unsafe
def get_unchecked(self) -> ~T:
269    @macros.unsafe
270    def get_unchecked(self) -> T:
271        """Get the contained value without checking if it was initialized.
272
273        Example
274        -------
275        ```py
276        cell = AsyncOnce[float]()
277        inner = cell.get_unchecked() # Undefined Behavior!!
278
279        # Initialize it first.
280        cell.get_or(math.sqrt(2.0))
281
282        # At this point of the program,
283        # it is guaranteed that the value is initialized.
284        inner = cell.get_unchecked()
285        ```
286        """
287        # SAFETY: The caller guarantees that the value is initialized.
288        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 without knowing the output is considered undefined behavior.

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

Clear the inner value, Setting it to None.

async def get_or(self, init: ~T) -> ~T:
323    async def get_or(self, init: T) -> T:
324        """Gets the contents of the cell, initializing it with `init` if the cell
325        was empty.
326
327        Many threads may call `get_or` concurrently with different
328        initializing functions, but it is guaranteed that only one function
329        will be executed.
330
331        Examples
332        --------
333        ```py
334        from sain.sync import AsyncOnce
335
336        cell = AsyncOnce[int]()
337        value = await cell.get_or(92)
338        assert value == 92
339
340        value = await cell.get_or(0)
341        assert value == 92
342        ```
343        """
344        # If the value is not empty we return it immediately.
345        if self._inner is not None:
346            return self._inner
347
348        if self._lock is None:
349            self._lock = asyncio.Lock()
350
351        async with self._lock:
352            self._inner = init
353            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:
355    async def get_or_with(self, f: collections.Callable[..., T]) -> T:
356        """Gets the contents of the cell, initializing it with `f` if the cell
357        was empty.
358
359        Many threads may call `get_or_with` concurrently with different
360        initializing functions, but it is guaranteed that only one function
361        will be executed.
362
363        Examples
364        --------
365        ```py
366        from sain.sync import AsyncOnce
367
368        cell = AsyncOnce[int]()
369        value = await cell.get_or_with(lambda: 92)
370        assert value == 92
371
372        value = await cell.get_or_init(lambda: 0)
373        assert value == 92
374        ```
375        """
376        # If the value is not empty we return it immediately.
377        if self._inner is not None:
378            return self._inner
379
380        if self._lock is None:
381            self._lock = asyncio.Lock()
382
383        async with self._lock:
384            v = f()
385            self._inner = v
386            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