sain

sain is a set of minimal abstraction that brings Rust's ecosystem to Python. It offers a few of the core Rust types like Vec<T> and Result<T, E> and more.

  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
 31"""sain is a set of minimal abstraction that brings Rust's ecosystem to Python. It offers a few of the core Rust types like `Vec<T>` and `Result<T, E>` and more."""
 32
 33from __future__ import annotations
 34
 35__all__ = (
 36    # cfg.py
 37    "cfg",
 38    "cfg_attr",
 39    # default.py
 40    "default",
 41    "Default",
 42    # option.py
 43    "option",
 44    "Some",
 45    "Option",
 46    "NOTHING",
 47    # iter.py
 48    "Iter",
 49    "Iterator",
 50    "iter",
 51    # macros.py
 52    "macros",
 53    "todo",
 54    "deprecated",
 55    "unimplemented",
 56    "doc",
 57    # futures.py
 58    "futures",
 59    # result.py
 60    "result",
 61    "Ok",
 62    "Err",
 63    "Result",
 64    # collections
 65    "collections",
 66    "Vec",
 67    # error.py
 68    "error",
 69    "Error",
 70    # boxed.py
 71    "boxed",
 72    "Box",
 73    # sync
 74    "sync",
 75    # maybe_uninit.py
 76    "maybe_uninit",
 77    # convert
 78    "convert",
 79    "From",
 80    "TryFrom",
 81    "Into",
 82    "TryInto",
 83    "ToString",
 84)
 85
 86from . import boxed
 87from . import collections
 88from . import convert
 89from . import default
 90from . import error
 91from . import futures
 92from . import iter
 93from . import macros
 94from . import maybe_uninit
 95from . import option
 96from . import result
 97from . import sync
 98from .boxed import Box
 99from .cfg import cfg
100from .cfg import cfg_attr
101from .collections import Vec
102from .convert import From
103from .convert import Into
104from .convert import ToString
105from .convert import TryFrom
106from .convert import TryInto
107from .default import Default
108from .error import Error
109from .iter import Iter
110from .iter import Iterator
111from .macros import deprecated
112from .macros import doc
113from .macros import todo
114from .macros import unimplemented
115from .option import NOTHING
116from .option import Option
117from .option import Some
118from .result import Err
119from .result import Ok
120from .result import Result
121
122__version__: str = "1.3.0"
123__url__: str = "https://github.com/nxtlo/sain"
124__author__: str = "nxtlo"
125__about__: str = (
126    "Sain is a dependency-free library that implements some of the Rust core"
127    "types which abstracts over common patterns in Rust for Python."
128)
129__license__: str = "BSD 3-Clause License"
@rustc_diagnostic_item('cfg')
def cfg( target_os: Optional[Literal['linux', 'win32', 'darwin', 'macos', 'unix', 'windows', 'ios']] = None, python_version: tuple[int, int, int] | None = None, target_arch: Optional[Literal['x86', 'x86_64', 'arm', 'arm64']] = None, impl: Optional[Literal['CPython', 'PyPy', 'IronPython', 'Jython']] = None) -> bool:
173@rustc_diagnostic_item("cfg")
174def cfg(
175    target_os: System | None = None,
176    python_version: tuple[int, int, int] | None = None,
177    target_arch: Arch | None = None,
178    impl: Python | None = None,
179) -> bool:
180    """A function that will run the code only if all predicate attributes returns `True`.
181
182    The difference between this function and `cfg_attr` is that this function will not raise an exception.
183    Instead it will return `False` if any of the attributes fails.
184
185    Example
186    -------
187    ```py
188    import sain
189
190    if cfg(target_os="windows"):
191        print("Windows")
192    elif cfg(target_os="linux", target_arch="arm64"):
193        print("Linux")
194    else:
195        print("Something else")
196    ```
197
198    Parameters
199    ----------
200    target_os : `str | None`
201        The targeted operating system thats required for the object to be ran.
202    python_version : `tuple[int, int, int] | None`
203        The targeted Python version thats required for the object to be ran. Format must be `(3, 9, 5)``
204    target_arch : `str | None`
205        The CPU targeted architecture thats required for the object to be ran.
206    impl : `str | None`
207        The Python implementation thats required for the object to be ran.
208
209    Returns
210    -------
211    `bool`
212        The condition that was checked.
213    """
214    checker = _AttrCheck(
215        lambda: None,
216        no_raise=True,
217        target_os=target_os,
218        python_version=python_version,
219        target_arch=target_arch,
220        impl=impl,
221    )
222    return checker.internal_check()

A function that will run the code only if all predicate attributes returns True.

The difference between this function and cfg_attr is that this function will not raise an exception. Instead it will return False if any of the attributes fails.

Example
import sain

if cfg(target_os="windows"):
    print("Windows")
elif cfg(target_os="linux", target_arch="arm64"):
    print("Linux")
else:
    print("Something else")
Parameters
  • target_os (str | None): The targeted operating system thats required for the object to be ran.
  • python_version (tuple[int, int, int] | None): The targeted Python version thats required for the object to be ran. Format must be `(3, 9, 5)``
  • target_arch (str | None): The CPU targeted architecture thats required for the object to be ran.
  • impl (str | None): The Python implementation thats required for the object to be ran.
Returns
  • bool: The condition that was checked.
  • # Implementations
  • **This function implements cfg:
@rustc_diagnostic_item('cfg_attr')
def cfg_attr( *, target_os: Optional[Literal['linux', 'win32', 'darwin', 'macos', 'unix', 'windows', 'ios']] = None, python_version: tuple[int, int, int] | None = None, target_arch: Optional[Literal['x86', 'x86_64', 'arm', 'arm64']] = None, impl: Optional[Literal['CPython', 'PyPy', 'IronPython', 'Jython']] = None) -> Callable[[~F], ~F]:
101@rustc_diagnostic_item("cfg_attr")
102def cfg_attr(
103    *,
104    target_os: System | None = None,
105    python_version: tuple[int, int, int] | None = None,
106    target_arch: Arch | None = None,
107    impl: Python | None = None,
108) -> collections.Callable[[F], F]:
109    """Conditional runtime object configuration based on passed arguments.
110
111    If the decorated object gets called and one of the attributes returns `False`,
112    `RuntimeError` will be raised and the object will not run.
113
114    Example
115    -------
116    ```py
117    import sain
118
119    @cfg_attr(target_os="windows")
120    def windows_only():
121        # Do stuff with Windows's API.
122        ...
123
124    # Mut be PyPy Python implementation or `RuntimeError` will be raised
125    # when creating the instance.
126    @cfg_attr(impl="PyPy")
127    class Zoo:
128        @sain.cfg_attr(target_os="linux")
129        def bark(self) -> None:
130            ...
131
132    # An instance will not be created if raised.
133    zoo = Zoo()
134    # RuntimeError("class Zoo requires PyPy implementation")
135    ```
136
137    Parameters
138    ----------
139    target_os : `str | None`
140        The targeted operating system thats required for the object.
141    python_version : `tuple[int, int, int] | None`
142        The targeted Python version thats required for the object. Format must be `(3, 9, 5)`.
143    target_arch : `str | None`
144        The CPU targeted architecture thats required for the object.
145    impl : `str | None`
146        The Python implementation thats required for the object.
147
148    Raises
149    ------
150    `RuntimeError`
151        This fails if any of the attributes returns `False`.
152    `ValueError`
153        If the passed Python implementation is unknown.
154    """
155
156    def decorator(callback: F) -> F:
157        @functools.wraps(callback)
158        def wrapper(*args: typing.Any, **kwargs: typing.Any) -> F:
159            checker = _AttrCheck(
160                callback,
161                target_os=target_os,
162                python_version=python_version,
163                target_arch=target_arch,
164                impl=impl,
165            )
166            return checker(*args, **kwargs)
167
168        return typing.cast(F, wrapper)
169
170    return decorator

Conditional runtime object configuration based on passed arguments.

If the decorated object gets called and one of the attributes returns False, RuntimeError will be raised and the object will not run.

Example
import sain

@cfg_attr(target_os="windows")
def windows_only():
    # Do stuff with Windows's API.
    ...

# Mut be PyPy Python implementation or `RuntimeError` will be raised
# when creating the instance.
@cfg_attr(impl="PyPy")
class Zoo:
    @sain.cfg_attr(target_os="linux")
    def bark(self) -> None:
        ...

# An instance will not be created if raised.
zoo = Zoo()
# RuntimeError("class Zoo requires PyPy implementation")
Parameters
  • target_os (str | None): The targeted operating system thats required for the object.
  • python_version (tuple[int, int, int] | None): The targeted Python version thats required for the object. Format must be (3, 9, 5).
  • target_arch (str | None): The CPU targeted architecture thats required for the object.
  • impl (str | None): The Python implementation thats required for the object.
Raises
  • RuntimeError: This fails if any of the attributes returns False.
  • ValueError: If the passed Python implementation is unknown.
  • # Implementations
  • **This function implements cfg_attr:
@typing.runtime_checkable
class Default(typing.Protocol[+_T_co]):
57@typing.runtime_checkable
58class Default(typing.Protocol[_T_co]):
59    """An interface for an object that has a default value.
60
61    Example
62    -------
63    ```py
64    from sain import Default
65
66    class Cache(Default[dict[str, Any]]):
67
68        @staticmethod
69        def default() -> dict[str, Any]:
70            return {}
71
72    cache = Cache.default()
73    print(cache)
74    assert isinstance(cache, Default)
75    # {}
76    ```
77    """
78
79    __slots__ = ()
80
81    @staticmethod
82    def default() -> _T_co:
83        """Return the default value of the object."""
84        raise NotImplementedError

An interface for an object that has a default value.

Example
from sain import Default

class Cache(Default[dict[str, Any]]):

    @staticmethod
    def default() -> dict[str, Any]:
        return {}

cache = Cache.default()
print(cache)
assert isinstance(cache, Default)
# {}
Default(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
@staticmethod
def default() -> +_T_co:
81    @staticmethod
82    def default() -> _T_co:
83        """Return the default value of the object."""
84        raise NotImplementedError

Return the default value of the object.

@typing.final
class Some(typing.Generic[~T], sain.Default[ForwardRef('Option[None]')]):
 55@typing.final
 56class Some(typing.Generic[T], _default.Default["Option[None]"]):
 57    """The `Option` type. An object that might be `T` or `None`.
 58
 59    It is a drop-in replacement for `typing.Optional[T]`, But has proper methods to handle the contained value.
 60
 61    Example
 62    -------
 63    ```py
 64    value = Some("Hello")
 65    print(value)
 66    # Some("Hello")
 67
 68    # This will unwrap the contained value as long as
 69    # it is not `None` otherwise this will raise an error.
 70    print(value.unwrap())
 71    # "Hello"
 72
 73    none_value = Some(None)
 74    while none_value.unwrap():
 75        # Never unreachable!
 76
 77    # Solving it with `unwrap_or` method to unwrap the value or return a default value.
 78    print(none_value.unwrap_or(10))
 79    # 10
 80    ```
 81    """
 82
 83    __slots__ = ("_value",)
 84    __match_args__ = ("_value",)
 85
 86    def __init__(self, value: T | None, /) -> None:
 87        self._value = value
 88
 89    @staticmethod
 90    def default() -> Option[None]:
 91        """Default value for `Some`. Returns `None` wrapped in `Some`.
 92
 93        Example
 94        -------
 95        ```py
 96        assert Some.default() == NOTHING
 97        ```
 98        """
 99        return NOTHING
100
101    # *- Reading the value -*
102
103    def transpose(self) -> T | None:
104        """Convert `Option[T]` into `T | None`.
105
106        Examples
107        --------
108        ```py
109        opt = Some('char')
110        x = opt.transpose()
111        assert x == 'char'
112
113        opt = Some(None)
114        assert opt.transpose() is None
115        ```
116        """
117        return self._value
118
119    def unwrap(self) -> T:
120        """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`.
121
122        It's usually not recommended to use this method in production code, since it raises.
123
124        Example
125        -------
126        ```py
127        value = Some(5)
128        print(value.unwrap())
129        # 5
130
131        value = Some(None)
132        print(value.unwrap())
133        # RuntimeError
134        ```
135
136        Raises
137        ------
138        `RuntimeError`
139            If the inner value is `None`.
140        """
141        if self._value is None:
142            raise RuntimeError("Called `Option.unwrap()` on `None`.") from None
143
144        return self._value
145
146    def unwrap_or(self, default: T, /) -> T:
147        """Unwrap the inner value either returning if its not `None` or returning `default`.
148
149        Example
150        -------
151        ```py
152        value = Some(5)
153        print(value.unwrap_or(10))
154        # 5
155
156        # Type hint is required here.
157        value: Option[int] = Some(None)
158        print(value.unwrap_or(10))
159        # 10
160        ```
161        """
162        if self._value is None:
163            return default
164
165        return self._value
166
167    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
168        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
169
170        Example
171        -------
172        ```py
173        value = Some(5)
174        print(value.unwrap_or_else(lambda: 10))
175        # 5
176
177        value: Option[bool] = Some(None)
178        print(value.unwrap_or_else(lambda: True))
179        # True
180        ```
181        """
182        if self._value is None:
183            return f()
184
185        return self._value
186
187    @macros.unsafe
188    def unwrap_unchecked(self) -> T:
189        """Returns the contained Some value without checking that the value is not None.
190
191        Example
192        -------
193        ```py
194        v: Option[float] = Some(1.2)
195        v.unwrap_unchecked() # 1.2
196
197        v: Option[float] = Some(None)
198        print(v.unwrap_unchecked()) # Undefined Behavior
199        ```
200        """
201        #! SAFETY: The caller guarantees that the value is not None.
202        return self._value  # pyright: ignore
203
204    def expect(self, message: str, /) -> T:
205        """Returns the contained value if it is not `None` otherwise raises a `RuntimeError`.
206
207        Example
208        -------
209        ```py
210        value = Some("Hello")
211
212        print(value.expect("Value is None"))
213        # "Hello"
214
215        value: Option[str] = Some(None)
216        print(value.expect("Value is None"))
217        # RuntimeError("Value is None")
218        ```
219        """
220        if self._value is None:
221            raise RuntimeError(message)
222
223        return self._value
224
225    # *- object transformation -*
226
227    def map(self, f: Fn[T, U], /) -> Option[U]:
228        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
229
230        Example
231        -------
232        ```py
233        value = Some(5.0)
234
235        print(value.map(lambda x: x * 2.0))
236        # Some(10.0)
237
238        value: Option[bool] = Some(None)
239        print(value)
240        # Some(None)
241        ```
242        """
243        if self._value is None:
244            return NOTHING  # pyright: ignore
245
246        return Some(f(self._value))
247
248    def map_or(self, default: U, f: Fn[T, U], /) -> U:
249        """Map the inner value to another type or return `default` if its `None`.
250
251        Example
252        -------
253        ```py
254        value: Option[float] = Some(5.0)
255
256        # map to int.
257        print(value.map_or(0, int))
258        # 6
259
260        value: Option[float] = Some(None)
261        print(value.map_or(0, int)
262        # 0
263        ```
264        """
265        if self._value is None:
266            return default
267
268        return f(self._value)
269
270    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
271        """Map the inner value to another type, or return `default()` if its `None`.
272
273        Example
274        -------
275        ```py
276        def default() -> int:
277            return sys.getsizeof(object())
278
279        value: Option[float] = Some(5.0)
280
281        # map to int.
282        print(value.map_or_else(default, int))
283        # 6
284
285        value: Option[float] = Some(None)
286        print(value.map_or_else(default, int)
287        # 28 <- size of object()
288        ```
289        """
290        if self._value is None:
291            return default()
292
293        return f(self._value)
294
295    def filter(self, predicate: Fn[T, bool]) -> Option[T]:
296        """Returns `Some(None)` if the contained value is `None`,
297
298        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
299
300        Example
301        -------
302        ```py
303        value = Some([1, 2, 3])
304
305        print(value.filter(lambda x: 1 in x))
306        # Some([1, 2, 3])
307
308        value: Option[int] = Some([1, 2, 3]) # or Some(None)
309        print(value.filter(lambda x: 1 not in x))
310        # None
311        ```
312        """
313        if (value := self._value) is not None:
314            if predicate(value):
315                return Some(value)
316
317        return NOTHING  # pyright: ignore
318
319    def ok_or(self, err: U) -> _result.Result[T, U]:
320        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
321
322        Example
323        -------
324        ```py
325        xyz: Option[str] = Some("foo")
326        assert xyz.ok_or(None) == Ok("foo")
327
328        xyz: Option[str] = Some(None)
329        assert xyz.ok_or(None) == Err(None)
330        ```
331        """
332        if self._value is None:
333            return _result.Err(err)
334
335        return _result.Ok(self._value)
336
337    def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]:
338        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`.
339
340        Example
341        -------
342        ```py
343        xyz: Option[str] = Some("foo")
344        assert xyz.ok_or(None) == Ok("foo")
345
346        xyz: Option[str] = Some(None)
347        assert xyz.ok_or(None) == Err(None)
348        ```
349        """
350        if self._value is None:
351            return _result.Err(err())
352
353        return _result.Ok(self._value)
354
355    def zip(self, other: Option[U]) -> Option[tuple[T, U]]:
356        """Zips `self` with `other`.
357
358        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`.
359
360        Example
361        -------
362        ```py
363        x = Some(1)
364        y = Some("hi")
365        z: Option[str] = Some(None)
366
367        assert x.zip(y) == Some((1, "hi"))
368        assert x.zip(z) == Some(None)
369        ```
370        """
371        if self._value is not None and other._value is not None:
372            return Some((self._value, other._value))
373
374        return NOTHING  # type: ignore
375
376    def zip_with(
377        self, other: Option[U], f: collections.Callable[[T, U], T_co]
378    ) -> Option[T_co]:
379        """Zips `self` with `other` using function `f`.
380
381        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`.
382
383        Example
384        -------
385        ```py
386        @dataclass
387        class Point:
388            x: float
389            y: float
390
391        x, y = Some(32.1), Some(42.4)
392        assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
393        ```
394        """
395        if self._value is not None and other._value is not None:
396            return Some(f(self._value, other._value))
397
398        return NOTHING  # type: ignore
399
400    # *- Inner operations *-
401
402    def take(self) -> Option[T]:
403        """Take the value from `Self`, Setting it to `None`.
404
405        Example
406        -------
407        ```py
408        original = Some("Hi")
409        new = original.take()
410
411        print(original, new)
412        # None, Some("Hi")
413        ```
414        """
415        if self._value is None:
416            return NOTHING  # pyright: ignore
417
418        val = self._value
419        self._value = None
420        return Some(val)
421
422    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
423        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
424
425        Example
426        -------
427        ```py
428        def validate(email: str) -> bool:
429            # you can obviously validate this better.
430            return email.find('@') == 1
431
432        original = Some("flex@gg.com")
433        valid = original.take_if(validate)
434        assert is_allowed.is_some() and original.is_none()
435
436        original = Some("mail.example.com")
437        invalid = original.take_if(validate)
438        assert invalid.is_none() and original.is_some()
439        ```
440        """
441        if self.map_or(False, predicate):
442            return self.take()
443
444        return NOTHING  # pyright: ignore
445
446    def replace(self, value: T) -> Option[T]:
447        """Replace the contained value with another value.
448
449        Use `Option.insert` if you want to return the original value
450        that got inserted instead of `self`
451
452        Example
453        -------
454        ```py
455        value: Option[str] = Some(None)
456        value.replace("Hello")
457        # Some("Hello")
458        ```
459        """
460        self._value = value
461        return self
462
463    def insert(self, value: T) -> T:
464        """Insert a value into the option, and then return a reference to it.
465
466        This will overwrite the old value if it was already contained.
467
468        Example
469        -------
470        ```py
471        flag: Option[bool] = Some(None)
472        flag_ref = flag.insert(True)
473        assert flag_ref == True
474        assert flag.unwrap() == True
475        ```
476        """
477        self._value = value
478        return value
479
480    def get_or_insert(self, value: T) -> T:
481        """Insert a value into the option if it was `None`,
482        and then return a reference to it.
483
484        Example
485        -------
486        ```py
487        state: Option[bool] = Some(None)
488        assert state.get_or_insert(True) is True
489        assert state.get_or_insert(False) is True
490        ```
491        """
492        if self._value is not None:
493            return self._value
494
495        self._value = value
496        return value
497
498    def get_or_insert_with(self, f: FnOnce[T]) -> T:
499        """Insert a value into the option computed from `f()` if it was `None`,
500        and then return a reference to it.
501
502        Example
503        -------
504        ```py
505        flag: Option[bool] = Some(None)
506        flag_ref = flag.insert(True)
507        assert flag_ref == True
508        assert flag.unwrap() == True
509        ```
510        """
511        if self._value is not None:
512            return self._value
513
514        v = self._value = f()
515        return v
516
517    def and_ok(self, optb: Option[T]) -> Option[T]:
518        """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`.
519
520        aliases: `Option::and`
521
522        Example
523        -------
524        ```py
525        x = Some(1)
526        y: Option[str] = Some(None)
527        assert x.and_ok(y) == Some(None)
528
529        x: Option[str] = Some(None)
530        y = Some(1)
531        assert x.and_ok(y) == Some(None)
532
533        y: Option[str] = Some("hi")
534        y = Some(100)
535        assert x.and_ok(y) == Some(100)
536        ```
537        """
538        if self._value is None or optb._value is None:
539            return optb
540
541        return NOTHING  # pyright: ignore
542
543    def and_then(self, f: Fn[T, Option[T]]) -> Option[T]:
544        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
545        on `T` and return `Option[T]`.
546
547        Example
548        -------
549        ```py
550        value = Some(5)
551        print(value.and_then(lambda x: Some(x * 2)))
552        # Some(10)
553
554        value: Option[int] = Some(None)
555        print(value.and_then(lambda x: Some(x * 2)))
556        # Some(None)
557        ```
558        """
559        if self._value is None:
560            return NOTHING  # pyright: ignore
561
562        return f(self._value)
563
564    def inspect(self, f: Fn[T, typing.Any]) -> Option[T]:
565        """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing.
566
567        Example
568        -------
569        ```py
570        def debug(x: str) -> None:
571            print("Debugging:", x)
572
573        value = Some("foo")
574        inner = value.inspect(debug).expect("no value to debug")
575        # prints: Debugging: "foo"
576
577        value: Option[str] = Some(None)
578        value.inspect(debug) # prints nothing
579        """
580        if self._value is not None:
581            f(self._value)
582
583        return self
584
585    # *- Builder methods *-
586
587    def iter(self) -> _iter.Iterator[T]:
588        """Returns an iterator over the contained value.
589
590        Example
591        -------
592        ```py
593        from sain import Some
594        value = Some("gg").iter()
595        assert value.next() == Some("gg")
596
597        value: Option[int] = Some(None)
598        assert value.iter().next().is_none()
599        ```
600        """
601        if self._value is None:
602            return _iter.empty()
603
604        return _iter.once(self._value)
605
606    # *- Boolean checks *-
607
608    def is_some(self) -> bool:
609        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
610
611        Example
612        -------
613        ```py
614        value = Some(5)
615        print(value.is_some())
616        # True
617
618        value: Option[int] = Some(None)
619        print(value.is_some())
620        # False
621        ```
622        """
623        return self._value is not None
624
625    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
626        """Returns `True` if the contained value is not `None` and
627        the predicate returns `True`, otherwise returns `False`.
628
629        Example
630        -------
631        ```py
632        value = Some(5)
633        print(value.is_some_and(lambda x: x > 3))
634        # True
635
636        value: Option[int] = Some(None)
637        print(value.is_some_and(lambda x: x > 3))
638        # False
639        ```
640        """
641        return self._value is not None and predicate(self._value)
642
643    def is_none(self) -> bool:
644        """Returns `True` if the contained value is `None`, otherwise returns `False`.
645
646        Example
647        -------
648        ```py
649        value = Some(5)
650        print(value.is_none())
651        # False
652
653        value: Option[int] = Some(None)
654        print(value.is_none())
655        # True
656        ```
657        """
658        return self._value is None
659
660    def is_none_or(self, f: Fn[T, bool]) -> bool:
661        """Returns `True` if the contained value is `None` or the predicate returns `True`,
662        otherwise returns `False`.
663
664        Example
665        -------
666        ```py
667        value = Some(5)
668        print(value.is_none_or(lambda x: x > 3))
669        # False
670
671        value: Option[int] = Some(None)
672        print(value.is_none_or(lambda x: x > 3))
673        # True
674        ```
675        """
676        match self._value:
677            case None:
678                return True
679            case x:
680                return f(x)
681
682    def __repr__(self) -> str:
683        if self._value is None:
684            return "None"
685        return f"Some({self._value!r})"
686
687    __str__ = __repr__
688
689    def __invert__(self) -> T:
690        return self.unwrap()
691
692    def __or__(self, other: T) -> T:
693        return self.unwrap_or(other)
694
695    def __bool__(self) -> bool:
696        return self.is_some()
697
698    def __eq__(self, other: object) -> bool:
699        if not isinstance(other, Some):
700            return NotImplemented
701
702        return self._value == other._value  # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
703
704    def __ne__(self, other: object) -> bool:
705        return not self.__eq__(other)
706
707    def __hash__(self) -> int:
708        return hash(self._value)

The Option type. An object that might be T or None.

It is a drop-in replacement for typing.Optional[T], But has proper methods to handle the contained value.

Example
value = Some("Hello")
print(value)
# Some("Hello")

# This will unwrap the contained value as long as
# it is not `None` otherwise this will raise an error.
print(value.unwrap())
# "Hello"

none_value = Some(None)
while none_value.unwrap():
    # Never unreachable!

# Solving it with `unwrap_or` method to unwrap the value or return a default value.
print(none_value.unwrap_or(10))
# 10
Some(value: Optional[~T], /)
86    def __init__(self, value: T | None, /) -> None:
87        self._value = value
@staticmethod
def default() -> 'Option[None]':
89    @staticmethod
90    def default() -> Option[None]:
91        """Default value for `Some`. Returns `None` wrapped in `Some`.
92
93        Example
94        -------
95        ```py
96        assert Some.default() == NOTHING
97        ```
98        """
99        return NOTHING

Default value for Some. Returns None wrapped in Some.

Example
assert Some.default() == NOTHING
def transpose(self) -> Optional[~T]:
103    def transpose(self) -> T | None:
104        """Convert `Option[T]` into `T | None`.
105
106        Examples
107        --------
108        ```py
109        opt = Some('char')
110        x = opt.transpose()
111        assert x == 'char'
112
113        opt = Some(None)
114        assert opt.transpose() is None
115        ```
116        """
117        return self._value

Convert Option[T] into T | None.

Examples
opt = Some('char')
x = opt.transpose()
assert x == 'char'

opt = Some(None)
assert opt.transpose() is None
def unwrap(self) -> ~T:
119    def unwrap(self) -> T:
120        """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`.
121
122        It's usually not recommended to use this method in production code, since it raises.
123
124        Example
125        -------
126        ```py
127        value = Some(5)
128        print(value.unwrap())
129        # 5
130
131        value = Some(None)
132        print(value.unwrap())
133        # RuntimeError
134        ```
135
136        Raises
137        ------
138        `RuntimeError`
139            If the inner value is `None`.
140        """
141        if self._value is None:
142            raise RuntimeError("Called `Option.unwrap()` on `None`.") from None
143
144        return self._value

Unwrap the inner value either returning if its not None or raising a RuntimeError.

It's usually not recommended to use this method in production code, since it raises.

Example
value = Some(5)
print(value.unwrap())
# 5

value = Some(None)
print(value.unwrap())
# RuntimeError
Raises
  • RuntimeError: If the inner value is None.
def unwrap_or(self, default: ~T, /) -> ~T:
146    def unwrap_or(self, default: T, /) -> T:
147        """Unwrap the inner value either returning if its not `None` or returning `default`.
148
149        Example
150        -------
151        ```py
152        value = Some(5)
153        print(value.unwrap_or(10))
154        # 5
155
156        # Type hint is required here.
157        value: Option[int] = Some(None)
158        print(value.unwrap_or(10))
159        # 10
160        ```
161        """
162        if self._value is None:
163            return default
164
165        return self._value

Unwrap the inner value either returning if its not None or returning default.

Example
value = Some(5)
print(value.unwrap_or(10))
# 5

# Type hint is required here.
value: Option[int] = Some(None)
print(value.unwrap_or(10))
# 10
def unwrap_or_else(self, f: Callable[[], ~T], /) -> ~T:
167    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
168        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
169
170        Example
171        -------
172        ```py
173        value = Some(5)
174        print(value.unwrap_or_else(lambda: 10))
175        # 5
176
177        value: Option[bool] = Some(None)
178        print(value.unwrap_or_else(lambda: True))
179        # True
180        ```
181        """
182        if self._value is None:
183            return f()
184
185        return self._value

Unwrap the inner value either returning if its not None or calling f to get a default value.

Example
value = Some(5)
print(value.unwrap_or_else(lambda: 10))
# 5

value: Option[bool] = Some(None)
print(value.unwrap_or_else(lambda: True))
# True
@macros.unsafe
def unwrap_unchecked(self) -> ~T:
187    @macros.unsafe
188    def unwrap_unchecked(self) -> T:
189        """Returns the contained Some value without checking that the value is not None.
190
191        Example
192        -------
193        ```py
194        v: Option[float] = Some(1.2)
195        v.unwrap_unchecked() # 1.2
196
197        v: Option[float] = Some(None)
198        print(v.unwrap_unchecked()) # Undefined Behavior
199        ```
200        """
201        #! SAFETY: The caller guarantees that the value is not None.
202        return self._value  # pyright: ignore

Returns the contained Some value without checking that the value is not None.

Example
v: Option[float] = Some(1.2)
v.unwrap_unchecked() # 1.2

v: Option[float] = Some(None)
print(v.unwrap_unchecked()) # Undefined Behavior

Safety ⚠️

Calling this method on None is considered undefined behavior.

def expect(self, message: str, /) -> ~T:
204    def expect(self, message: str, /) -> T:
205        """Returns the contained value if it is not `None` otherwise raises a `RuntimeError`.
206
207        Example
208        -------
209        ```py
210        value = Some("Hello")
211
212        print(value.expect("Value is None"))
213        # "Hello"
214
215        value: Option[str] = Some(None)
216        print(value.expect("Value is None"))
217        # RuntimeError("Value is None")
218        ```
219        """
220        if self._value is None:
221            raise RuntimeError(message)
222
223        return self._value

Returns the contained value if it is not None otherwise raises a RuntimeError.

Example
value = Some("Hello")

print(value.expect("Value is None"))
# "Hello"

value: Option[str] = Some(None)
print(value.expect("Value is None"))
# RuntimeError("Value is None")
def map(self, f: Callable[[~T], ~U], /) -> 'Option[U]':
227    def map(self, f: Fn[T, U], /) -> Option[U]:
228        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
229
230        Example
231        -------
232        ```py
233        value = Some(5.0)
234
235        print(value.map(lambda x: x * 2.0))
236        # Some(10.0)
237
238        value: Option[bool] = Some(None)
239        print(value)
240        # Some(None)
241        ```
242        """
243        if self._value is None:
244            return NOTHING  # pyright: ignore
245
246        return Some(f(self._value))

Map the inner value to another type. Returning Some(None) if T is None.

Example
value = Some(5.0)

print(value.map(lambda x: x * 2.0))
# Some(10.0)

value: Option[bool] = Some(None)
print(value)
# Some(None)
def map_or(self, default: ~U, f: Callable[[~T], ~U], /) -> ~U:
248    def map_or(self, default: U, f: Fn[T, U], /) -> U:
249        """Map the inner value to another type or return `default` if its `None`.
250
251        Example
252        -------
253        ```py
254        value: Option[float] = Some(5.0)
255
256        # map to int.
257        print(value.map_or(0, int))
258        # 6
259
260        value: Option[float] = Some(None)
261        print(value.map_or(0, int)
262        # 0
263        ```
264        """
265        if self._value is None:
266            return default
267
268        return f(self._value)

Map the inner value to another type or return default if its None.

Example
value: Option[float] = Some(5.0)

# map to int.
print(value.map_or(0, int))
# 6

value: Option[float] = Some(None)
print(value.map_or(0, int)
# 0
def map_or_else(self, default: Callable[[], ~U], f: Callable[[~T], ~U], /) -> ~U:
270    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
271        """Map the inner value to another type, or return `default()` if its `None`.
272
273        Example
274        -------
275        ```py
276        def default() -> int:
277            return sys.getsizeof(object())
278
279        value: Option[float] = Some(5.0)
280
281        # map to int.
282        print(value.map_or_else(default, int))
283        # 6
284
285        value: Option[float] = Some(None)
286        print(value.map_or_else(default, int)
287        # 28 <- size of object()
288        ```
289        """
290        if self._value is None:
291            return default()
292
293        return f(self._value)

Map the inner value to another type, or return default() if its None.

Example
def default() -> int:
    return sys.getsizeof(object())

value: Option[float] = Some(5.0)

# map to int.
print(value.map_or_else(default, int))
# 6

value: Option[float] = Some(None)
print(value.map_or_else(default, int)
# 28 <- size of object()
def filter(self, predicate: Callable[[~T], bool]) -> 'Option[T]':
295    def filter(self, predicate: Fn[T, bool]) -> Option[T]:
296        """Returns `Some(None)` if the contained value is `None`,
297
298        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
299
300        Example
301        -------
302        ```py
303        value = Some([1, 2, 3])
304
305        print(value.filter(lambda x: 1 in x))
306        # Some([1, 2, 3])
307
308        value: Option[int] = Some([1, 2, 3]) # or Some(None)
309        print(value.filter(lambda x: 1 not in x))
310        # None
311        ```
312        """
313        if (value := self._value) is not None:
314            if predicate(value):
315                return Some(value)
316
317        return NOTHING  # pyright: ignore

Returns Some(None) if the contained value is None,

otherwise calls the predicate and returns Some(T) if the predicate returns True.

Example
value = Some([1, 2, 3])

print(value.filter(lambda x: 1 in x))
# Some([1, 2, 3])

value: Option[int] = Some([1, 2, 3]) # or Some(None)
print(value.filter(lambda x: 1 not in x))
# None
def ok_or(self, err: ~U) -> '_result.Result[T, U]':
319    def ok_or(self, err: U) -> _result.Result[T, U]:
320        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
321
322        Example
323        -------
324        ```py
325        xyz: Option[str] = Some("foo")
326        assert xyz.ok_or(None) == Ok("foo")
327
328        xyz: Option[str] = Some(None)
329        assert xyz.ok_or(None) == Err(None)
330        ```
331        """
332        if self._value is None:
333            return _result.Err(err)
334
335        return _result.Ok(self._value)

Transforms the Option<T> into a Result<T, E>, mapping Some(v) to Ok(v) and None to Err(err).

Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")

xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
def ok_or_else(self, err: Callable[[], ~U]) -> '_result.Result[T, U]':
337    def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]:
338        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`.
339
340        Example
341        -------
342        ```py
343        xyz: Option[str] = Some("foo")
344        assert xyz.ok_or(None) == Ok("foo")
345
346        xyz: Option[str] = Some(None)
347        assert xyz.ok_or(None) == Err(None)
348        ```
349        """
350        if self._value is None:
351            return _result.Err(err())
352
353        return _result.Ok(self._value)

Transforms the Option<T> into a Result<T, E>, mapping Some(v) to Ok(v) and None to Err(err()).

Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")

xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
def zip(self, other: 'Option[U]') -> 'Option[tuple[T, U]]':
355    def zip(self, other: Option[U]) -> Option[tuple[T, U]]:
356        """Zips `self` with `other`.
357
358        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`.
359
360        Example
361        -------
362        ```py
363        x = Some(1)
364        y = Some("hi")
365        z: Option[str] = Some(None)
366
367        assert x.zip(y) == Some((1, "hi"))
368        assert x.zip(z) == Some(None)
369        ```
370        """
371        if self._value is not None and other._value is not None:
372            return Some((self._value, other._value))
373
374        return NOTHING  # type: ignore

Zips self with other.

if self is Some(s) and other is Some(o), this returns Some((s, o)) otherwise None.

Example
x = Some(1)
y = Some("hi")
z: Option[str] = Some(None)

assert x.zip(y) == Some((1, "hi"))
assert x.zip(z) == Some(None)
def zip_with(self, other: 'Option[U]', f: Callable[[~T, ~U], +T_co]) -> 'Option[T_co]':
376    def zip_with(
377        self, other: Option[U], f: collections.Callable[[T, U], T_co]
378    ) -> Option[T_co]:
379        """Zips `self` with `other` using function `f`.
380
381        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`.
382
383        Example
384        -------
385        ```py
386        @dataclass
387        class Point:
388            x: float
389            y: float
390
391        x, y = Some(32.1), Some(42.4)
392        assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
393        ```
394        """
395        if self._value is not None and other._value is not None:
396            return Some(f(self._value, other._value))
397
398        return NOTHING  # type: ignore

Zips self with other using function f.

if self is Some(s) and other is Some(o), this returns Some(f(s, o)) otherwise None.

Example
@dataclass
class Point:
    x: float
    y: float

x, y = Some(32.1), Some(42.4)
assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
def take(self) -> 'Option[T]':
402    def take(self) -> Option[T]:
403        """Take the value from `Self`, Setting it to `None`.
404
405        Example
406        -------
407        ```py
408        original = Some("Hi")
409        new = original.take()
410
411        print(original, new)
412        # None, Some("Hi")
413        ```
414        """
415        if self._value is None:
416            return NOTHING  # pyright: ignore
417
418        val = self._value
419        self._value = None
420        return Some(val)

Take the value from Self, Setting it to None.

Example
original = Some("Hi")
new = original.take()

print(original, new)
# None, Some("Hi")
def take_if(self, predicate: Callable[[~T], bool]) -> 'Option[T]':
422    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
423        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
424
425        Example
426        -------
427        ```py
428        def validate(email: str) -> bool:
429            # you can obviously validate this better.
430            return email.find('@') == 1
431
432        original = Some("flex@gg.com")
433        valid = original.take_if(validate)
434        assert is_allowed.is_some() and original.is_none()
435
436        original = Some("mail.example.com")
437        invalid = original.take_if(validate)
438        assert invalid.is_none() and original.is_some()
439        ```
440        """
441        if self.map_or(False, predicate):
442            return self.take()
443
444        return NOTHING  # pyright: ignore

Take the value from Self, Setting it to None only if predicate returns True.

Example
def validate(email: str) -> bool:
    # you can obviously validate this better.
    return email.find('@') == 1

original = Some("flex@gg.com")
valid = original.take_if(validate)
assert is_allowed.is_some() and original.is_none()

original = Some("mail.example.com")
invalid = original.take_if(validate)
assert invalid.is_none() and original.is_some()
def replace(self, value: ~T) -> 'Option[T]':
446    def replace(self, value: T) -> Option[T]:
447        """Replace the contained value with another value.
448
449        Use `Option.insert` if you want to return the original value
450        that got inserted instead of `self`
451
452        Example
453        -------
454        ```py
455        value: Option[str] = Some(None)
456        value.replace("Hello")
457        # Some("Hello")
458        ```
459        """
460        self._value = value
461        return self

Replace the contained value with another value.

Use Option.insert if you want to return the original value that got inserted instead of self

Example
value: Option[str] = Some(None)
value.replace("Hello")
# Some("Hello")
def insert(self, value: ~T) -> ~T:
463    def insert(self, value: T) -> T:
464        """Insert a value into the option, and then return a reference to it.
465
466        This will overwrite the old value if it was already contained.
467
468        Example
469        -------
470        ```py
471        flag: Option[bool] = Some(None)
472        flag_ref = flag.insert(True)
473        assert flag_ref == True
474        assert flag.unwrap() == True
475        ```
476        """
477        self._value = value
478        return value

Insert a value into the option, and then return a reference to it.

This will overwrite the old value if it was already contained.

Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
def get_or_insert(self, value: ~T) -> ~T:
480    def get_or_insert(self, value: T) -> T:
481        """Insert a value into the option if it was `None`,
482        and then return a reference to it.
483
484        Example
485        -------
486        ```py
487        state: Option[bool] = Some(None)
488        assert state.get_or_insert(True) is True
489        assert state.get_or_insert(False) is True
490        ```
491        """
492        if self._value is not None:
493            return self._value
494
495        self._value = value
496        return value

Insert a value into the option if it was None, and then return a reference to it.

Example
state: Option[bool] = Some(None)
assert state.get_or_insert(True) is True
assert state.get_or_insert(False) is True
def get_or_insert_with(self, f: Callable[[], ~T]) -> ~T:
498    def get_or_insert_with(self, f: FnOnce[T]) -> T:
499        """Insert a value into the option computed from `f()` if it was `None`,
500        and then return a reference to it.
501
502        Example
503        -------
504        ```py
505        flag: Option[bool] = Some(None)
506        flag_ref = flag.insert(True)
507        assert flag_ref == True
508        assert flag.unwrap() == True
509        ```
510        """
511        if self._value is not None:
512            return self._value
513
514        v = self._value = f()
515        return v

Insert a value into the option computed from f() if it was None, and then return a reference to it.

Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
def and_ok(self, optb: 'Option[T]') -> 'Option[T]':
517    def and_ok(self, optb: Option[T]) -> Option[T]:
518        """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`.
519
520        aliases: `Option::and`
521
522        Example
523        -------
524        ```py
525        x = Some(1)
526        y: Option[str] = Some(None)
527        assert x.and_ok(y) == Some(None)
528
529        x: Option[str] = Some(None)
530        y = Some(1)
531        assert x.and_ok(y) == Some(None)
532
533        y: Option[str] = Some("hi")
534        y = Some(100)
535        assert x.and_ok(y) == Some(100)
536        ```
537        """
538        if self._value is None or optb._value is None:
539            return optb
540
541        return NOTHING  # pyright: ignore

Returns None if self or optb is None, otherwise return optb.

aliases: Option::and

Example
x = Some(1)
y: Option[str] = Some(None)
assert x.and_ok(y) == Some(None)

x: Option[str] = Some(None)
y = Some(1)
assert x.and_ok(y) == Some(None)

y: Option[str] = Some("hi")
y = Some(100)
assert x.and_ok(y) == Some(100)
def and_then(self, f: 'Fn[T, Option[T]]') -> 'Option[T]':
543    def and_then(self, f: Fn[T, Option[T]]) -> Option[T]:
544        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
545        on `T` and return `Option[T]`.
546
547        Example
548        -------
549        ```py
550        value = Some(5)
551        print(value.and_then(lambda x: Some(x * 2)))
552        # Some(10)
553
554        value: Option[int] = Some(None)
555        print(value.and_then(lambda x: Some(x * 2)))
556        # Some(None)
557        ```
558        """
559        if self._value is None:
560            return NOTHING  # pyright: ignore
561
562        return f(self._value)

Returns Some(None) if the contained value is None, otherwise call f() on T and return Option[T].

Example
value = Some(5)
print(value.and_then(lambda x: Some(x * 2)))
# Some(10)

value: Option[int] = Some(None)
print(value.and_then(lambda x: Some(x * 2)))
# Some(None)
def inspect(self, f: Callable[[~T], typing.Any]) -> 'Option[T]':
564    def inspect(self, f: Fn[T, typing.Any]) -> Option[T]:
565        """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing.
566
567        Example
568        -------
569        ```py
570        def debug(x: str) -> None:
571            print("Debugging:", x)
572
573        value = Some("foo")
574        inner = value.inspect(debug).expect("no value to debug")
575        # prints: Debugging: "foo"
576
577        value: Option[str] = Some(None)
578        value.inspect(debug) # prints nothing
579        """
580        if self._value is not None:
581            f(self._value)
582
583        return self

Calls f() on the contained value if it was Some(v), otherwise does nothing.

Example

```py def debug(x: str) -> None: print("Debugging:", x)

value = Some("foo") inner = value.inspect(debug).expect("no value to debug")

prints: Debugging: "foo"

value: Option[str] = Some(None) value.inspect(debug) # prints nothing

def iter(self) -> Iterator[~T]:
587    def iter(self) -> _iter.Iterator[T]:
588        """Returns an iterator over the contained value.
589
590        Example
591        -------
592        ```py
593        from sain import Some
594        value = Some("gg").iter()
595        assert value.next() == Some("gg")
596
597        value: Option[int] = Some(None)
598        assert value.iter().next().is_none()
599        ```
600        """
601        if self._value is None:
602            return _iter.empty()
603
604        return _iter.once(self._value)

Returns an iterator over the contained value.

Example
from sain import Some
value = Some("gg")sain.iter()
assert value.next() == Some("gg")

value: Option[int] = Some(None)
assert value.iter().next().is_none()
def is_some(self) -> bool:
608    def is_some(self) -> bool:
609        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
610
611        Example
612        -------
613        ```py
614        value = Some(5)
615        print(value.is_some())
616        # True
617
618        value: Option[int] = Some(None)
619        print(value.is_some())
620        # False
621        ```
622        """
623        return self._value is not None

Returns True if the contained value is not None, otherwise returns False.

Example
value = Some(5)
print(value.is_some())
# True

value: Option[int] = Some(None)
print(value.is_some())
# False
def is_some_and(self, predicate: Callable[[~T], bool]) -> bool:
625    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
626        """Returns `True` if the contained value is not `None` and
627        the predicate returns `True`, otherwise returns `False`.
628
629        Example
630        -------
631        ```py
632        value = Some(5)
633        print(value.is_some_and(lambda x: x > 3))
634        # True
635
636        value: Option[int] = Some(None)
637        print(value.is_some_and(lambda x: x > 3))
638        # False
639        ```
640        """
641        return self._value is not None and predicate(self._value)

Returns True if the contained value is not None and the predicate returns True, otherwise returns False.

Example
value = Some(5)
print(value.is_some_and(lambda x: x > 3))
# True

value: Option[int] = Some(None)
print(value.is_some_and(lambda x: x > 3))
# False
def is_none(self) -> bool:
643    def is_none(self) -> bool:
644        """Returns `True` if the contained value is `None`, otherwise returns `False`.
645
646        Example
647        -------
648        ```py
649        value = Some(5)
650        print(value.is_none())
651        # False
652
653        value: Option[int] = Some(None)
654        print(value.is_none())
655        # True
656        ```
657        """
658        return self._value is None

Returns True if the contained value is None, otherwise returns False.

Example
value = Some(5)
print(value.is_none())
# False

value: Option[int] = Some(None)
print(value.is_none())
# True
def is_none_or(self, f: Callable[[~T], bool]) -> bool:
660    def is_none_or(self, f: Fn[T, bool]) -> bool:
661        """Returns `True` if the contained value is `None` or the predicate returns `True`,
662        otherwise returns `False`.
663
664        Example
665        -------
666        ```py
667        value = Some(5)
668        print(value.is_none_or(lambda x: x > 3))
669        # False
670
671        value: Option[int] = Some(None)
672        print(value.is_none_or(lambda x: x > 3))
673        # True
674        ```
675        """
676        match self._value:
677            case None:
678                return True
679            case x:
680                return f(x)

Returns True if the contained value is None or the predicate returns True, otherwise returns False.

Example
value = Some(5)
print(value.is_none_or(lambda x: x > 3))
# False

value: Option[int] = Some(None)
print(value.is_none_or(lambda x: x > 3))
# True
Option = 'Some[T]'
NOTHING = None
@typing.final
@diagnostic
class Iter(sain.Iterator[~Item]):
862@typing.final
863# @rustc_diagnostic_item("Iter")
864@diagnostic
865class Iter(Iterator[Item]):
866    """a lazy iterator that has its items ready in-memory.
867
868    This is similar to Rust `std::slice::Iter<T>` item which iterables can build
869    from this via `.iter()` method.
870
871    Example
872    -------
873    ```py
874    iterator = Iter([1, 2, 3])
875
876    # Limit the results to 2.
877    for item in iterator.take(2):
878        print(item)
879    # 1
880    # 2
881
882    # Filter the results.
883    for item in iterator.filter(lambda item: item > 1):
884        print(item)
885    # 2
886    # 3
887    # 3
888
889    # Indexing is supported.
890    print(iterator[0])
891    # 1
892    ```
893
894    Parameters
895    ----------
896    items: `Iterable[Item]`
897        The items to iterate over. This can be anything that implements `__iter__` and `__next__`.
898    """
899
900    __slots__ = ("_it",)
901
902    def __init__(self, iterable: collections.Iterable[Item]) -> None:
903        self._it = iter(iterable)
904
905    def clone(self) -> Iter[Item]:
906        """Return a copy of this iterator.
907
908        ```py
909        it = Iterator([1, 2, 3])
910
911        for i in it.clone():
912            ...
913
914        # The actual iterator hasn't been exhausted.
915        assert it.count() == 3
916        ```
917        """
918        return Iter(copy.copy(self._it))
919
920    def __next__(self) -> Item:
921        try:
922            return next(self._it)
923        except StopIteration:
924            raise
925
926    def __getitem__(self, index: int) -> Option[Item]:
927        try:
928            return self.skip(index).first()
929        except IndexError:
930            unreachable()
931
932    def __contains__(self, item: Item) -> bool:
933        return item in self._it

a lazy iterator that has its items ready in-memory.

This is similar to Rust std::slice::Iter<T> item which iterables can build from this via sain.iter method.

Example
iterator = Iter([1, 2, 3])

# Limit the results to 2.
for item in iterator.take(2):
    print(item)
# 1
# 2

# Filter the results.
for item in iterator.filter(lambda item: item > 1):
    print(item)
# 2
# 3
# 3

# Indexing is supported.
print(iterator[0])
# 1
Parameters
  • items (Iterable[Item]): The items to iterate over. This can be anything that implements __iter__ and __next__.
Iter(iterable: Iterable[~Item])
902    def __init__(self, iterable: collections.Iterable[Item]) -> None:
903        self._it = iter(iterable)
def clone(self) -> Iter[~Item]:
905    def clone(self) -> Iter[Item]:
906        """Return a copy of this iterator.
907
908        ```py
909        it = Iterator([1, 2, 3])
910
911        for i in it.clone():
912            ...
913
914        # The actual iterator hasn't been exhausted.
915        assert it.count() == 3
916        ```
917        """
918        return Iter(copy.copy(self._it))

Return a copy of this iterator.

it = Iterator([1, 2, 3])

for i in it.clone():
    ...

# The actual iterator hasn't been exhausted.
assert it.count() == 3
class Iterator(typing.Generic[~Item], abc.ABC, sain.Default[ForwardRef('Empty[Item]')]):
115class Iterator(
116    typing.Generic[Item],
117    abc.ABC,
118    _default.Default["Empty[Item]"],
119):
120    """An abstract interface for dealing with iterators.
121
122    This is exactly the same trait as `core::iter::Iterator` trait from Rust.
123
124    This is the main interface that any type can implement by basically inheriting from it.
125    The method `__next__` is the only method that needs to be implemented, You get all the other methods for free.
126
127    If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementors
128    and type hints.
129
130    Example
131    -------
132    ```py
133    @dataclass
134    class Counter(Iterator[int]):
135        start: int = 0
136        stop: int | None = None
137
138        # implement the required method.
139        def __next__(self) -> int:
140            result = self.start
141            self.start += 1
142
143            if self.stop is not None and result >= self.stop:
144                raise StopIteration
145
146            return result
147
148    counter = Counter(start=0, stop=10)
149    for i in counter.map(lambda x: x * 2): # multiply each number
150        ...
151    ```
152    """
153
154    @abc.abstractmethod
155    def __next__(self) -> Item:
156        raise NotImplementedError
157
158    ###################
159    # const functions #
160    ###################
161
162    @staticmethod
163    @typing.final
164    def default() -> Empty[Item]:
165        """Return the default iterator for this type. It returns an empty iterator.
166
167        Example
168        -------
169        ```py
170        it: Iterator[int] = Iter.default()
171        assert t.next().is_none()
172        ```
173        """
174        return Empty()
175
176    @typing.overload
177    @typing.final
178    def collect(self) -> collections.Sequence[Item]: ...
179
180    @typing.overload
181    @typing.final
182    def collect(
183        self, *, cast: collections.Callable[[Item], OtherItem]
184    ) -> collections.Sequence[OtherItem]: ...
185
186    @typing.final
187    def collect(
188        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
189    ) -> collections.Sequence[Item] | collections.Sequence[OtherItem]:
190        """Collects all items in the iterator into an immutable sequence.
191
192        Example
193        -------
194        ```py
195        iterator = Iter(range(3))
196        iterator.collect()
197        # (0, 1, 2, 3)
198        iterator.collect(cast=str) # Map each element and collect it.
199        # ('0', '1', '2', '3')
200        ```
201
202        Parameters
203        ----------
204        cast: `T | None`
205            An optional type to cast the items into.
206            If not provided the items will be returned as it's original type.
207        """
208        if cast is not None:
209            return tuple(cast(i) for i in self)
210
211        return tuple(_ for _ in self)
212
213    @typing.final
214    def collect_into(self, collection: Collector[Item]) -> None:
215        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
216
217        Example
218        -------
219        ```py
220        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
221        uniques = set()
222        iterator.collect_into(uniques)
223        # assert uniques == {1, 2, 3, 4, 6}
224        ```
225
226        Parameters
227        ----------
228        collection: `MutableSequence[T]` | `set[T]`
229            The collection to extend the items in this iterator with.
230        """
231        if isinstance(collection, collections.MutableSequence):
232            collection.extend(_ for _ in self)
233        elif isinstance(collection, collections.MutableSet):
234            collection.update(_ for _ in self)
235        else:
236            for idx, item in enumerate(self):
237                collection[idx] = item
238
239    @typing.final
240    def to_vec(self) -> vec.Vec[Item]:
241        """Convert this iterator into `Vec[T]`.
242
243        Example
244        -------
245        ```py
246        it = sain.iter.once(0)
247        vc = it.to_vec()
248
249        assert to_vec == [0]
250        ```
251        """
252        return vec.Vec(_ for _ in self)
253
254    @typing.final
255    def sink(self) -> None:
256        """Consume all elements from this iterator, flushing it into the sink.
257
258        Example
259        -------
260        ```py
261        it = Iter((1, 2, 3))
262        it.sink()
263        assert it.next().is_none()
264        ```
265        """
266        for _ in self:
267            pass
268
269    @typing.final
270    def raw_parts(self) -> collections.Generator[Item, None, None]:
271        """Decompose all elements from this iterator, yielding it one by one
272        as a normal generator.
273
274        This mainly used for objects that needs to satisfy its exact type.
275
276        ```py
277        it = Iter("cba")
278        sort = sorted(it.raw_parts())
279
280        assert it.count() == 0
281        assert sort == ["a", "b", "c"]
282        ```
283        """
284        for item in self:
285            yield item
286
287    ##################
288    # default impl's #
289    ##################
290
291    def next(self) -> Option[Item]:
292        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
293
294        Example
295        -------
296        ```py
297        iterator = Iter(["1", "2"])
298        assert iterator.next() == Some("1")
299        assert iterator.next() == Some("2")
300        assert iterator.next().is_none()
301        ```
302        """
303        try:
304            return _option.Some(self.__next__())
305        except StopIteration:
306            # ! SAFETY: No more items in the iterator.
307            return _option.NOTHING  # pyright: ignore
308
309    def cloned(self) -> Cloned[Item]:
310        """Creates an iterator which shallow copies its elements by reference.
311
312        If you need a copy of the actual iterator and not the elements.
313        use `Iter.clone()`
314
315        .. note::
316            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
317            on each item that is being yielded.
318
319        Example
320        -------
321        ```py
322        @dataclass
323        class User:
324            users_ids: list[int] = []
325
326        # An iterator which elements points to the same user.
327        user = User()
328        it = Iter((user, user))
329
330        for u in it.cloned():
331            u.user_ids.append(1)
332
333        # We iterated over the same user pointer twice and appended "1"
334        # since `copy` returns a shallow copy of nested structures.
335        assert len(user.user_ids) == 2
336        ```
337        """
338        return Cloned(self)
339
340    def copied(self) -> Copied[Item]:
341        """Creates an iterator which copies all of its elements by value.
342
343        If you only need a copy of the item reference, Use `.cloned()` instead.
344
345        .. note::
346            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
347            on each item that is being yielded.
348
349        Example
350        -------
351        ```py
352        @dataclass
353        class User:
354            users_ids: list[int] = []
355
356        # An iterator which elements points to the same user.
357        user = User()
358        it = Iter((user, user))
359
360        for u in it.copied():
361            # A new list is created for each item.
362            u.user_ids.append(1)
363
364        # The actual list is untouched since we consumed a deep copy of it.
365        assert len(user.user_ids) == 0
366        ```
367        """
368        return Copied(self)
369
370    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
371        """Maps each item in the iterator to another type.
372
373        Example
374        -------
375        ```py
376        iterator = Iter(["1", "2", "3"]).map(int)
377
378        for item in iterator:
379            assert isinstance(item, int)
380        ```
381
382        Parameters
383        ----------
384        predicate: `Callable[[Item], OtherItem]`
385            The function to map each item in the iterator to the other type.
386        """
387        return Map(self, fn)
388
389    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
390        """Filters the iterator to only yield items that match the predicate.
391
392        Example
393        -------
394        ```py
395        places = Iter(['London', 'Paris', 'Los Angeles'])
396        for place in places.filter(lambda place: place.startswith('L')):
397            print(place)
398
399        # London
400        # Los Angeles
401        ```
402        """
403        return Filter(self, predicate)
404
405    def take(self, count: int) -> Take[Item]:
406        """Take the first number of items until the number of items
407        are yielded or the end of the iterator is exhausted.
408
409        Example
410        -------
411        ```py
412        iterator = Iter(['c', 'x', 'y'])
413
414        for x in iterator.take(2):
415            assert x in ('c', 'x')
416
417        # <Iter(['c', 'x'])>
418        ```
419        """
420        return Take(self, count)
421
422    def skip(self, count: int) -> Skip[Item]:
423        """Skips the first number of items in the iterator.
424
425        Example
426        -------
427        ```py
428        iterator = Iter((1, 2, 3, 4))
429        for i in iterator.skip(2):
430            print(i)
431
432        # 3
433        # 4
434        ```
435        """
436        return Skip(self, count)
437
438    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
439        """Create a new iterator that yields a tuple of the index and item.
440
441        Example
442        -------
443        ```py
444        iterator = Iter([1, 2, 3])
445        for index, item in iterator.enumerate():
446            print(index, item)
447
448        # 0 1
449        # 1 2
450        # 2 3
451        ```
452        """
453        return Enumerate(self, start)
454
455    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
456        """yields items from the iterator while predicate returns `True`.
457
458        The rest of the items are discarded as soon as the predicate returns `False`
459
460        Example
461        -------
462        ```py
463        iterator = Iter(['a', 'ab', 'xd', 'ba'])
464        for x in iterator.take_while(lambda x: 'a' in x):
465            print(x)
466
467        # a
468        # ab
469        ```
470
471        Parameters
472        ----------
473        predicate: `collections.Callable[[Item], bool]`
474            The function to predicate each item in the iterator.
475        """
476        return TakeWhile(self, f)
477
478    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
479        """Yields items from the iterator while predicate returns `False`.
480
481        Example
482        -------
483        ```py
484        iterator = Iter(['a', 'ab', 'xd', 'ba'])
485        for x in iterator.drop_while(lambda x: 'a' in x):
486            print(x)
487
488        # xd
489        # ba
490        ```
491
492        Parameters
493        ----------
494        predicate: `collections.Callable[[Item], bool]`
495            The function to predicate each item in the iterator.
496        """
497        return DropWhile(self, f)
498
499    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
500        """Returns an iterator over `chunk_size` elements of the iterator at a time,
501        starting at the beginning of the iterator.
502
503        Example
504        -------
505        ```py
506        iter = Iter(['a', 'b', 'c', 'd', 'e'])
507        chunks = iter.chunks()
508        assert chunks.next().unwrap() == ['a', 'b']
509        assert chunks.next().unwrap() == ['c', 'd']
510        assert chunks.next().unwrap() == ['e']
511        assert chunks.next().is_none()
512        ```
513        """
514        return Chunks(self, chunk_size)
515
516    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
517        """Return `True` if all items in the iterator match the predicate.
518
519        Example
520        -------
521        ```py
522        iterator = Iter([1, 2, 3])
523        while iterator.all(lambda item: isinstance(item, int)):
524            print("Still all integers")
525            continue
526            # Still all integers
527        ```
528
529        Parameters
530        ----------
531        predicate: `collections.Callable[[Item], bool]`
532            The function to test each item in the iterator.
533        """
534        return all(predicate(item) for item in self)
535
536    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
537        """`True` if any items in the iterator match the predicate.
538
539        Example
540        -------
541        ```py
542        iterator = Iter([1, 2, 3])
543        if iterator.any(lambda item: isinstance(item, int)):
544            print("At least one item is an int.")
545        # At least one item is an int.
546        ```
547
548        Parameters
549        ----------
550        predicate: `collections.Callable[[Item], bool]`
551            The function to test each item in the iterator.
552        """
553        return any(predicate(item) for item in self)
554
555    def zip(
556        self, other: collections.Iterable[OtherItem]
557    ) -> Iterator[tuple[Item, OtherItem]]:
558        """Zips the iterator with another iterable.
559
560        Example
561        -------
562        ```py
563        iterator = Iter([1, 2, 3])
564        for item, other_item in iterator.zip([4, 5, 6]):
565            assert item == other_item
566        <Iter([(1, 4), (2, 5), (3, 6)])>
567        ```
568
569        Parameters
570        ----------
571        other: `Iter[OtherItem]`
572            The iterable to zip with.
573
574        Returns
575        -------
576        `Iter[tuple[Item, OtherItem]]`
577            The zipped iterator.
578
579        """
580        return Iter(zip(self.raw_parts(), other))
581
582    def sort(
583        self,
584        *,
585        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
586        reverse: bool = False,
587    ) -> Iterator[Item]:
588        """Sorts the iterator.
589
590        Example
591        -------
592        ```py
593        iterator = Iter([3, 1, 6, 7])
594        for item in iterator.sort(key=lambda item: item < 3):
595            print(item)
596        # 1
597        # 3
598        # 6
599        # 7
600        ```
601
602        Parameters
603        ----------
604        key: `collections.Callable[[Item], Any]`
605            The function to sort by.
606        reverse: `bool`
607            Whether to reverse the sort.
608
609        """
610        return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))
611
612    def reversed(self) -> Iterator[Item]:
613        """Returns a new iterator that yields the items in the iterator in reverse order.
614
615        Example
616        -------
617        ```py
618        iterator = Iter([3, 1, 6, 7])
619        for item in iterator.reversed():
620            print(item)
621        # 7
622        # 6
623        # 1
624        # 3
625        ```
626        """
627        # NOTE: In order to reverse the iterator we need to
628        # first collect it into some collection.
629        return Iter(reversed(self.collect()))
630
631    def union(self, other: collections.Iterable[Item]) -> Iterator[Item]:
632        """Returns a new iterator that yields all items from both iterators.
633
634        Example
635        -------
636        ```py
637        iterator = Iter([1, 2, 3])
638        other = [4, 5, 6]
639
640        for item in iterator.union(other):
641            print(item)
642        # 1
643        # 2
644        # 3
645        # 4
646        # 5
647        # 6
648        ```
649
650        Parameters
651        ----------
652        other: `Iter[Item]`
653            The iterable to union with.
654        """
655        return Iter(itertools.chain(self.raw_parts(), other))
656
657    def first(self) -> Option[Item]:
658        """Returns the first item in the iterator.
659
660        Example
661        -------
662        ```py
663        iterator = Iter([3, 1, 6, 7])
664        iterator.first().is_some_and(lambda x: x == 3)
665        ```
666        """
667        return self.take(1).next()
668
669    def last(self) -> Option[Item]:
670        """Returns the last item in the iterator.
671
672        Example
673        -------
674        ```py
675        iterator = Iter([3, 1, 6, 7])
676        iterator.last().is_some_and(lambda x: x == 7)
677        ```
678        """
679        return self.reversed().first()
680
681    def count(self) -> int:
682        """Return the count of elements in memory this iterator has.
683
684        Example
685        -------
686        ```py
687        it = Iter(range(3))
688        assert it.count() == 3
689        ```
690        """
691        count = 0
692        for _ in self:
693            count += 1
694
695        return count
696
697    def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]:
698        """Searches for an element of an iterator that satisfies a predicate.
699
700        If you want the position of the element, use `Iterator.position` instead.
701
702        `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
703        and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.
704
705        Example
706        -------
707        ```py
708        it = Iter(range(10))
709        item = it.find(lambda num: num > 5)
710        print(item) # 6
711        ```
712        """
713        for item in self:
714            if predicate(item):
715                return _option.Some(item)
716
717        # no more items
718        return _option.NOTHING  # pyright: ignore
719
720    def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]:
721        """Searches for the position of an element in the iterator that satisfies a predicate.
722
723        If you want the object itself, use `Iterator.find` instead.
724
725        `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
726        and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.
727
728        Example
729        -------
730        ```py
731        it = Iter(range(10))
732        position = it.find(lambda num: num > 5)
733        assert position.unwrap() == 6
734        ```
735        """
736        for position, value in self.enumerate():
737            if predicate(value):
738                return _option.Some(position)
739
740        # no more items
741        return _option.NOTHING  # pyright: ignore
742
743    def fold(
744        self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem]
745    ) -> OtherItem:
746        """Folds every element into an accumulator by applying an operation, returning the final result.
747
748        fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element.
749        The closure returns the value that the accumulator should have for the next iteration.
750
751        The initial value is the value the accumulator will have on the first call.
752
753        After applying this closure to every element of the iterator, fold() returns the accumulator.
754
755        This operation is sometimes called ‘reduce’ or ‘inject’.
756
757        Example
758        -------
759        ```py
760        a = Iter([1, 2, 3, 4])
761        sum = a.fold(0, lambda acc, elem: acc + elem)
762        assert sum == 10
763        ```
764        """
765        accum = init
766        while True:
767            try:
768                x = self.__next__()
769                accum = f(accum, x)
770            except StopIteration:
771                break
772
773        return accum
774
775    def sum(self: Sum) -> int:
776        """Sums an iterator of a possible type that can be converted to an integer.
777
778        Example
779        -------
780        ```py
781        numbers: Iterator[str] = Iter(["1", "2", "3"])
782        total = numbers.sum()
783        assert total == 6
784        ```
785        """
786        return sum(int(_) for _ in self)
787
788    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
789        """Calls `func` on each item in the iterator.
790
791        Example
792        -------
793        ```py
794        iterator = Iter([1, 2, 3])
795        iterator.for_each(lambda item: print(item))
796        # 1
797        # 2
798        # 3
799        ```
800
801        Parameters
802        ----------
803        func: `collections.Callable[[Item], typing.Any]`
804            The function to call on each item in the iterator.
805        """
806        for item in self:
807            func(item)
808
809    async def async_for_each(
810        self,
811        func: collections.Callable[
812            [Item], collections.Coroutine[None, typing.Any, OtherItem]
813        ],
814    ) -> _result.Result[collections.Sequence[OtherItem], futures.SpawnError]:
815        """Calls the async function on each item in the iterator concurrently.
816
817        Example
818        -------
819        ```py
820        async def create_user(username: str) -> None:
821            async with aiohttp.request("POST", f'.../{username}') as r:
822                return await r.json()
823
824        async def main():
825            users = sain.into_iter(["danny", "legalia"])
826            results = await users.async_for_each(lambda username: create_user(username))
827            for k, v in results.unwrap().items():
828                ...
829        ```
830
831        Parameters
832        ----------
833        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
834            The async function to call on each item in the iterator.
835        """
836        return await futures.spawn(*(func(item) for item in self))
837
838    def __reversed__(self) -> Iterator[Item]:
839        return self.reversed()
840
841    def __setitem__(self, _: None, __: None) -> typing.NoReturn:
842        raise NotImplementedError(
843            f"{type(self).__name__} doesn't support item assignment."
844        ) from None
845
846    def __repr__(self) -> str:
847        return "<Iterator>"
848
849    def __copy__(self) -> Cloned[Item]:
850        return self.cloned()
851
852    def __deepcopy__(self) -> Copied[Item]:
853        return self.copied()
854
855    def __len__(self) -> int:
856        return self.count()
857
858    def __iter__(self) -> Iterator[Item]:
859        return self

An abstract interface for dealing with iterators.

This is exactly the same trait as core::iter::Iterator trait from Rust.

This is the main interface that any type can implement by basically inheriting from it. The method __next__ is the only method that needs to be implemented, You get all the other methods for free.

If you want to use a ready iterator for general purposes, Use Iter. This interface is only for implementors and type hints.

Example
@dataclass
class Counter(Iterator[int]):
    start: int = 0
    stop: int | None = None

    # implement the required method.
    def __next__(self) -> int:
        result = self.start
        self.start += 1

        if self.stop is not None and result >= self.stop:
            raise StopIteration

        return result

counter = Counter(start=0, stop=10)
for i in counter.map(lambda x: x * 2): # multiply each number
    ...
@staticmethod
@typing.final
def default() -> sain.iter.Empty[~Item]:
162    @staticmethod
163    @typing.final
164    def default() -> Empty[Item]:
165        """Return the default iterator for this type. It returns an empty iterator.
166
167        Example
168        -------
169        ```py
170        it: Iterator[int] = Iter.default()
171        assert t.next().is_none()
172        ```
173        """
174        return Empty()

Return the default iterator for this type. It returns an empty iterator.

Example
it: Iterator[int] = Iter.default()
assert t.next().is_none()
@typing.final
def collect( self, *, cast: Callable[[~Item], +OtherItem] | None = None) -> Sequence[~Item] | Sequence[+OtherItem]:
186    @typing.final
187    def collect(
188        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
189    ) -> collections.Sequence[Item] | collections.Sequence[OtherItem]:
190        """Collects all items in the iterator into an immutable sequence.
191
192        Example
193        -------
194        ```py
195        iterator = Iter(range(3))
196        iterator.collect()
197        # (0, 1, 2, 3)
198        iterator.collect(cast=str) # Map each element and collect it.
199        # ('0', '1', '2', '3')
200        ```
201
202        Parameters
203        ----------
204        cast: `T | None`
205            An optional type to cast the items into.
206            If not provided the items will be returned as it's original type.
207        """
208        if cast is not None:
209            return tuple(cast(i) for i in self)
210
211        return tuple(_ for _ in self)

Collects all items in the iterator into an immutable sequence.

Example
iterator = Iter(range(3))
iterator.collect()
# (0, 1, 2, 3)
iterator.collect(cast=str) # Map each element and collect it.
# ('0', '1', '2', '3')
Parameters
  • cast (T | None): An optional type to cast the items into. If not provided the items will be returned as it's original type.
@typing.final
def collect_into(self, collection: 'Collector[Item]') -> None:
213    @typing.final
214    def collect_into(self, collection: Collector[Item]) -> None:
215        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
216
217        Example
218        -------
219        ```py
220        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
221        uniques = set()
222        iterator.collect_into(uniques)
223        # assert uniques == {1, 2, 3, 4, 6}
224        ```
225
226        Parameters
227        ----------
228        collection: `MutableSequence[T]` | `set[T]`
229            The collection to extend the items in this iterator with.
230        """
231        if isinstance(collection, collections.MutableSequence):
232            collection.extend(_ for _ in self)
233        elif isinstance(collection, collections.MutableSet):
234            collection.update(_ for _ in self)
235        else:
236            for idx, item in enumerate(self):
237                collection[idx] = item

Consume this iterator, extending all items in the iterator into a mutable collection.

Example
iterator = Iter([1, 1, 2, 3, 4, 2, 6])
uniques = set()
iterator.collect_into(uniques)
# assert uniques == {1, 2, 3, 4, 6}
Parameters
  • collection (MutableSequence[T] | set[T]): The collection to extend the items in this iterator with.
@typing.final
def to_vec(self) -> Vec[~Item]:
239    @typing.final
240    def to_vec(self) -> vec.Vec[Item]:
241        """Convert this iterator into `Vec[T]`.
242
243        Example
244        -------
245        ```py
246        it = sain.iter.once(0)
247        vc = it.to_vec()
248
249        assert to_vec == [0]
250        ```
251        """
252        return vec.Vec(_ for _ in self)

Convert this iterator into Vec[T].

Example
it = sain.iter.once(0)
vc = it.to_vec()

assert to_vec == [0]
@typing.final
def sink(self) -> None:
254    @typing.final
255    def sink(self) -> None:
256        """Consume all elements from this iterator, flushing it into the sink.
257
258        Example
259        -------
260        ```py
261        it = Iter((1, 2, 3))
262        it.sink()
263        assert it.next().is_none()
264        ```
265        """
266        for _ in self:
267            pass

Consume all elements from this iterator, flushing it into the sink.

Example
it = Iter((1, 2, 3))
it.sink()
assert it.next().is_none()
@typing.final
def raw_parts(self) -> Generator[~Item, None, None]:
269    @typing.final
270    def raw_parts(self) -> collections.Generator[Item, None, None]:
271        """Decompose all elements from this iterator, yielding it one by one
272        as a normal generator.
273
274        This mainly used for objects that needs to satisfy its exact type.
275
276        ```py
277        it = Iter("cba")
278        sort = sorted(it.raw_parts())
279
280        assert it.count() == 0
281        assert sort == ["a", "b", "c"]
282        ```
283        """
284        for item in self:
285            yield item

Decompose all elements from this iterator, yielding it one by one as a normal generator.

This mainly used for objects that needs to satisfy its exact type.

it = Iter("cba")
sort = sorted(it.raw_parts())

assert it.count() == 0
assert sort == ["a", "b", "c"]
def next(self) -> 'Option[Item]':
291    def next(self) -> Option[Item]:
292        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
293
294        Example
295        -------
296        ```py
297        iterator = Iter(["1", "2"])
298        assert iterator.next() == Some("1")
299        assert iterator.next() == Some("2")
300        assert iterator.next().is_none()
301        ```
302        """
303        try:
304            return _option.Some(self.__next__())
305        except StopIteration:
306            # ! SAFETY: No more items in the iterator.
307            return _option.NOTHING  # pyright: ignore

Advance the iterator, Returning the next item, Some(None) if all items yielded.

Example
iterator = Iter(["1", "2"])
assert iterator.next() == Some("1")
assert iterator.next() == Some("2")
assert iterator.next().is_none()
def cloned(self) -> sain.iter.Cloned[~Item]:
309    def cloned(self) -> Cloned[Item]:
310        """Creates an iterator which shallow copies its elements by reference.
311
312        If you need a copy of the actual iterator and not the elements.
313        use `Iter.clone()`
314
315        .. note::
316            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
317            on each item that is being yielded.
318
319        Example
320        -------
321        ```py
322        @dataclass
323        class User:
324            users_ids: list[int] = []
325
326        # An iterator which elements points to the same user.
327        user = User()
328        it = Iter((user, user))
329
330        for u in it.cloned():
331            u.user_ids.append(1)
332
333        # We iterated over the same user pointer twice and appended "1"
334        # since `copy` returns a shallow copy of nested structures.
335        assert len(user.user_ids) == 2
336        ```
337        """
338        return Cloned(self)

Creates an iterator which shallow copies its elements by reference.

If you need a copy of the actual iterator and not the elements. use Iter.clone()

This method calls copy.copy() on each item that is being yielded.

Example
@dataclass
class User:
    users_ids: list[int] = []

# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))

for u in it.cloned():
    u.user_ids.append(1)

# We iterated over the same user pointer twice and appended "1"
# since `copy` returns a shallow copy of nested structures.
assert len(user.user_ids) == 2
def copied(self) -> sain.iter.Copied[~Item]:
340    def copied(self) -> Copied[Item]:
341        """Creates an iterator which copies all of its elements by value.
342
343        If you only need a copy of the item reference, Use `.cloned()` instead.
344
345        .. note::
346            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
347            on each item that is being yielded.
348
349        Example
350        -------
351        ```py
352        @dataclass
353        class User:
354            users_ids: list[int] = []
355
356        # An iterator which elements points to the same user.
357        user = User()
358        it = Iter((user, user))
359
360        for u in it.copied():
361            # A new list is created for each item.
362            u.user_ids.append(1)
363
364        # The actual list is untouched since we consumed a deep copy of it.
365        assert len(user.user_ids) == 0
366        ```
367        """
368        return Copied(self)

Creates an iterator which copies all of its elements by value.

If you only need a copy of the item reference, Use .cloned() instead.

This method simply calls copy.deepcopy() on each item that is being yielded.

Example
@dataclass
class User:
    users_ids: list[int] = []

# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))

for u in it.copied():
    # A new list is created for each item.
    u.user_ids.append(1)

# The actual list is untouched since we consumed a deep copy of it.
assert len(user.user_ids) == 0
def map( self, fn: Callable[[~Item], +OtherItem]) -> sain.iter.Map[~Item, +OtherItem]:
370    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
371        """Maps each item in the iterator to another type.
372
373        Example
374        -------
375        ```py
376        iterator = Iter(["1", "2", "3"]).map(int)
377
378        for item in iterator:
379            assert isinstance(item, int)
380        ```
381
382        Parameters
383        ----------
384        predicate: `Callable[[Item], OtherItem]`
385            The function to map each item in the iterator to the other type.
386        """
387        return Map(self, fn)

Maps each item in the iterator to another type.

Example
iterator = Iter(["1", "2", "3"]).map(int)

for item in iterator:
    assert isinstance(item, int)
Parameters
  • predicate (Callable[[Item], OtherItem]): The function to map each item in the iterator to the other type.
def filter(self, predicate: Callable[[~Item], bool]) -> sain.iter.Filter[~Item]:
389    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
390        """Filters the iterator to only yield items that match the predicate.
391
392        Example
393        -------
394        ```py
395        places = Iter(['London', 'Paris', 'Los Angeles'])
396        for place in places.filter(lambda place: place.startswith('L')):
397            print(place)
398
399        # London
400        # Los Angeles
401        ```
402        """
403        return Filter(self, predicate)

Filters the iterator to only yield items that match the predicate.

Example
places = Iter(['London', 'Paris', 'Los Angeles'])
for place in places.filter(lambda place: place.startswith('L')):
    print(place)

# London
# Los Angeles
def take(self, count: int) -> sain.iter.Take[~Item]:
405    def take(self, count: int) -> Take[Item]:
406        """Take the first number of items until the number of items
407        are yielded or the end of the iterator is exhausted.
408
409        Example
410        -------
411        ```py
412        iterator = Iter(['c', 'x', 'y'])
413
414        for x in iterator.take(2):
415            assert x in ('c', 'x')
416
417        # <Iter(['c', 'x'])>
418        ```
419        """
420        return Take(self, count)

Take the first number of items until the number of items are yielded or the end of the iterator is exhausted.

Example
iterator = Iter(['c', 'x', 'y'])

for x in iterator.take(2):
    assert x in ('c', 'x')

# <Iter(['c', 'x'])>
def skip(self, count: int) -> sain.iter.Skip[~Item]:
422    def skip(self, count: int) -> Skip[Item]:
423        """Skips the first number of items in the iterator.
424
425        Example
426        -------
427        ```py
428        iterator = Iter((1, 2, 3, 4))
429        for i in iterator.skip(2):
430            print(i)
431
432        # 3
433        # 4
434        ```
435        """
436        return Skip(self, count)

Skips the first number of items in the iterator.

Example
iterator = Iter((1, 2, 3, 4))
for i in iterator.skip(2):
    print(i)

# 3
# 4
def enumerate(self, *, start: int = 0) -> sain.iter.Enumerate[~Item]:
438    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
439        """Create a new iterator that yields a tuple of the index and item.
440
441        Example
442        -------
443        ```py
444        iterator = Iter([1, 2, 3])
445        for index, item in iterator.enumerate():
446            print(index, item)
447
448        # 0 1
449        # 1 2
450        # 2 3
451        ```
452        """
453        return Enumerate(self, start)

Create a new iterator that yields a tuple of the index and item.

Example
iterator = Iter([1, 2, 3])
for index, item in iterator.enumerate():
    print(index, item)

# 0 1
# 1 2
# 2 3
def take_while(self, f: Callable[[~Item], bool]) -> sain.iter.TakeWhile[~Item]:
455    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
456        """yields items from the iterator while predicate returns `True`.
457
458        The rest of the items are discarded as soon as the predicate returns `False`
459
460        Example
461        -------
462        ```py
463        iterator = Iter(['a', 'ab', 'xd', 'ba'])
464        for x in iterator.take_while(lambda x: 'a' in x):
465            print(x)
466
467        # a
468        # ab
469        ```
470
471        Parameters
472        ----------
473        predicate: `collections.Callable[[Item], bool]`
474            The function to predicate each item in the iterator.
475        """
476        return TakeWhile(self, f)

yields items from the iterator while predicate returns True.

The rest of the items are discarded as soon as the predicate returns False

Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.take_while(lambda x: 'a' in x):
    print(x)

# a
# ab
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
def drop_while(self, f: Callable[[~Item], bool]) -> sain.iter.DropWhile[~Item]:
478    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
479        """Yields items from the iterator while predicate returns `False`.
480
481        Example
482        -------
483        ```py
484        iterator = Iter(['a', 'ab', 'xd', 'ba'])
485        for x in iterator.drop_while(lambda x: 'a' in x):
486            print(x)
487
488        # xd
489        # ba
490        ```
491
492        Parameters
493        ----------
494        predicate: `collections.Callable[[Item], bool]`
495            The function to predicate each item in the iterator.
496        """
497        return DropWhile(self, f)

Yields items from the iterator while predicate returns False.

Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.drop_while(lambda x: 'a' in x):
    print(x)

# xd
# ba
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
def chunks(self, chunk_size: int, /) -> sain.iter.Chunks[~Item]:
499    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
500        """Returns an iterator over `chunk_size` elements of the iterator at a time,
501        starting at the beginning of the iterator.
502
503        Example
504        -------
505        ```py
506        iter = Iter(['a', 'b', 'c', 'd', 'e'])
507        chunks = iter.chunks()
508        assert chunks.next().unwrap() == ['a', 'b']
509        assert chunks.next().unwrap() == ['c', 'd']
510        assert chunks.next().unwrap() == ['e']
511        assert chunks.next().is_none()
512        ```
513        """
514        return Chunks(self, chunk_size)

Returns an iterator over chunk_size elements of the iterator at a time, starting at the beginning of the iterator.

Example
iter = Iter(['a', 'b', 'c', 'd', 'e'])
chunks = iter.chunks()
assert chunks.next().unwrap() == ['a', 'b']
assert chunks.next().unwrap() == ['c', 'd']
assert chunks.next().unwrap() == ['e']
assert chunks.next().is_none()
def all(self, predicate: Callable[[~Item], bool]) -> bool:
516    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
517        """Return `True` if all items in the iterator match the predicate.
518
519        Example
520        -------
521        ```py
522        iterator = Iter([1, 2, 3])
523        while iterator.all(lambda item: isinstance(item, int)):
524            print("Still all integers")
525            continue
526            # Still all integers
527        ```
528
529        Parameters
530        ----------
531        predicate: `collections.Callable[[Item], bool]`
532            The function to test each item in the iterator.
533        """
534        return all(predicate(item) for item in self)

Return True if all items in the iterator match the predicate.

Example
iterator = Iter([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
    # Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
def any(self, predicate: Callable[[~Item], bool]) -> bool:
536    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
537        """`True` if any items in the iterator match the predicate.
538
539        Example
540        -------
541        ```py
542        iterator = Iter([1, 2, 3])
543        if iterator.any(lambda item: isinstance(item, int)):
544            print("At least one item is an int.")
545        # At least one item is an int.
546        ```
547
548        Parameters
549        ----------
550        predicate: `collections.Callable[[Item], bool]`
551            The function to test each item in the iterator.
552        """
553        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

Example
iterator = Iter([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
    print("At least one item is an int.")
# At least one item is an int.
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
def zip( self, other: Iterable[+OtherItem]) -> Iterator[tuple[~Item, +OtherItem]]:
555    def zip(
556        self, other: collections.Iterable[OtherItem]
557    ) -> Iterator[tuple[Item, OtherItem]]:
558        """Zips the iterator with another iterable.
559
560        Example
561        -------
562        ```py
563        iterator = Iter([1, 2, 3])
564        for item, other_item in iterator.zip([4, 5, 6]):
565            assert item == other_item
566        <Iter([(1, 4), (2, 5), (3, 6)])>
567        ```
568
569        Parameters
570        ----------
571        other: `Iter[OtherItem]`
572            The iterable to zip with.
573
574        Returns
575        -------
576        `Iter[tuple[Item, OtherItem]]`
577            The zipped iterator.
578
579        """
580        return Iter(zip(self.raw_parts(), other))

Zips the iterator with another iterable.

Example
iterator = Iter([1, 2, 3])
for item, other_item in iterator.zip([4, 5, 6]):
    assert item == other_item
<Iter([(1, 4), (2, 5), (3, 6)])>
Parameters
  • other (Iter[OtherItem]): The iterable to zip with.
Returns
  • Iter[tuple[Item, OtherItem]]: The zipped iterator.
def sort( self, *, key: 'collections.Callable[[Item], _typeshed.SupportsRichComparison]', reverse: bool = False) -> Iterator[~Item]:
582    def sort(
583        self,
584        *,
585        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
586        reverse: bool = False,
587    ) -> Iterator[Item]:
588        """Sorts the iterator.
589
590        Example
591        -------
592        ```py
593        iterator = Iter([3, 1, 6, 7])
594        for item in iterator.sort(key=lambda item: item < 3):
595            print(item)
596        # 1
597        # 3
598        # 6
599        # 7
600        ```
601
602        Parameters
603        ----------
604        key: `collections.Callable[[Item], Any]`
605            The function to sort by.
606        reverse: `bool`
607            Whether to reverse the sort.
608
609        """
610        return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.sort(key=lambda item: item < 3):
    print(item)
# 1
# 3
# 6
# 7
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
def reversed(self) -> Iterator[~Item]:
612    def reversed(self) -> Iterator[Item]:
613        """Returns a new iterator that yields the items in the iterator in reverse order.
614
615        Example
616        -------
617        ```py
618        iterator = Iter([3, 1, 6, 7])
619        for item in iterator.reversed():
620            print(item)
621        # 7
622        # 6
623        # 1
624        # 3
625        ```
626        """
627        # NOTE: In order to reverse the iterator we need to
628        # first collect it into some collection.
629        return Iter(reversed(self.collect()))

Returns a new iterator that yields the items in the iterator in reverse order.

Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.reversed():
    print(item)
# 7
# 6
# 1
# 3
def union(self, other: Iterable[~Item]) -> Iterator[~Item]:
631    def union(self, other: collections.Iterable[Item]) -> Iterator[Item]:
632        """Returns a new iterator that yields all items from both iterators.
633
634        Example
635        -------
636        ```py
637        iterator = Iter([1, 2, 3])
638        other = [4, 5, 6]
639
640        for item in iterator.union(other):
641            print(item)
642        # 1
643        # 2
644        # 3
645        # 4
646        # 5
647        # 6
648        ```
649
650        Parameters
651        ----------
652        other: `Iter[Item]`
653            The iterable to union with.
654        """
655        return Iter(itertools.chain(self.raw_parts(), other))

Returns a new iterator that yields all items from both iterators.

Example
iterator = Iter([1, 2, 3])
other = [4, 5, 6]

for item in iterator.union(other):
    print(item)
# 1
# 2
# 3
# 4
# 5
# 6
Parameters
  • other (Iter[Item]): The iterable to union with.
def first(self) -> 'Option[Item]':
657    def first(self) -> Option[Item]:
658        """Returns the first item in the iterator.
659
660        Example
661        -------
662        ```py
663        iterator = Iter([3, 1, 6, 7])
664        iterator.first().is_some_and(lambda x: x == 3)
665        ```
666        """
667        return self.take(1).next()

Returns the first item in the iterator.

Example
iterator = Iter([3, 1, 6, 7])
iterator.first().is_some_and(lambda x: x == 3)
def last(self) -> 'Option[Item]':
669    def last(self) -> Option[Item]:
670        """Returns the last item in the iterator.
671
672        Example
673        -------
674        ```py
675        iterator = Iter([3, 1, 6, 7])
676        iterator.last().is_some_and(lambda x: x == 7)
677        ```
678        """
679        return self.reversed().first()

Returns the last item in the iterator.

Example
iterator = Iter([3, 1, 6, 7])
iterator.last().is_some_and(lambda x: x == 7)
def count(self) -> int:
681    def count(self) -> int:
682        """Return the count of elements in memory this iterator has.
683
684        Example
685        -------
686        ```py
687        it = Iter(range(3))
688        assert it.count() == 3
689        ```
690        """
691        count = 0
692        for _ in self:
693            count += 1
694
695        return count

Return the count of elements in memory this iterator has.

Example
it = Iter(range(3))
assert it.count() == 3
def find(self, predicate: Callable[[~Item], bool]) -> 'Option[Item]':
697    def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]:
698        """Searches for an element of an iterator that satisfies a predicate.
699
700        If you want the position of the element, use `Iterator.position` instead.
701
702        `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
703        and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.
704
705        Example
706        -------
707        ```py
708        it = Iter(range(10))
709        item = it.find(lambda num: num > 5)
710        print(item) # 6
711        ```
712        """
713        for item in self:
714            if predicate(item):
715                return _option.Some(item)
716
717        # no more items
718        return _option.NOTHING  # pyright: ignore

Searches for an element of an iterator that satisfies a predicate.

If you want the position of the element, use Iterator.position instead.

find() takes a lambda that returns true or false. It applies this closure to each element of the iterator, and if any of them return true, then find() returns Some(element). If they all return false, it returns None.

Example
it = Iter(range(10))
item = it.find(lambda num: num > 5)
print(item) # 6
def position(self, predicate: Callable[[~Item], bool]) -> 'Option[int]':
720    def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]:
721        """Searches for the position of an element in the iterator that satisfies a predicate.
722
723        If you want the object itself, use `Iterator.find` instead.
724
725        `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
726        and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.
727
728        Example
729        -------
730        ```py
731        it = Iter(range(10))
732        position = it.find(lambda num: num > 5)
733        assert position.unwrap() == 6
734        ```
735        """
736        for position, value in self.enumerate():
737            if predicate(value):
738                return _option.Some(position)
739
740        # no more items
741        return _option.NOTHING  # pyright: ignore

Searches for the position of an element in the iterator that satisfies a predicate.

If you want the object itself, use Iterator.find instead.

position() takes a lambda that returns true or false. It applies this closure to each element of the iterator, and if any of them return true, then position() returns Some(position_of_element). If they all return false, it returns None.

Example
it = Iter(range(10))
position = it.find(lambda num: num > 5)
assert position.unwrap() == 6
def fold( self, init: +OtherItem, f: Callable[[+OtherItem, ~Item], +OtherItem]) -> +OtherItem:
743    def fold(
744        self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem]
745    ) -> OtherItem:
746        """Folds every element into an accumulator by applying an operation, returning the final result.
747
748        fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element.
749        The closure returns the value that the accumulator should have for the next iteration.
750
751        The initial value is the value the accumulator will have on the first call.
752
753        After applying this closure to every element of the iterator, fold() returns the accumulator.
754
755        This operation is sometimes called ‘reduce’ or ‘inject’.
756
757        Example
758        -------
759        ```py
760        a = Iter([1, 2, 3, 4])
761        sum = a.fold(0, lambda acc, elem: acc + elem)
762        assert sum == 10
763        ```
764        """
765        accum = init
766        while True:
767            try:
768                x = self.__next__()
769                accum = f(accum, x)
770            except StopIteration:
771                break
772
773        return accum

Folds every element into an accumulator by applying an operation, returning the final result.

fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. The closure returns the value that the accumulator should have for the next iteration.

The initial value is the value the accumulator will have on the first call.

After applying this closure to every element of the iterator, fold() returns the accumulator.

This operation is sometimes called ‘reduce’ or ‘inject’.

Example
a = Iter([1, 2, 3, 4])
sum = a.fold(0, lambda acc, elem: acc + elem)
assert sum == 10
def sum(self: 'Sum') -> int:
775    def sum(self: Sum) -> int:
776        """Sums an iterator of a possible type that can be converted to an integer.
777
778        Example
779        -------
780        ```py
781        numbers: Iterator[str] = Iter(["1", "2", "3"])
782        total = numbers.sum()
783        assert total == 6
784        ```
785        """
786        return sum(int(_) for _ in self)

Sums an iterator of a possible type that can be converted to an integer.

Example
numbers: Iterator[str] = Iter(["1", "2", "3"])
total = numbers.sum()
assert total == 6
def for_each(self, func: Callable[[~Item], typing.Any]) -> None:
788    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
789        """Calls `func` on each item in the iterator.
790
791        Example
792        -------
793        ```py
794        iterator = Iter([1, 2, 3])
795        iterator.for_each(lambda item: print(item))
796        # 1
797        # 2
798        # 3
799        ```
800
801        Parameters
802        ----------
803        func: `collections.Callable[[Item], typing.Any]`
804            The function to call on each item in the iterator.
805        """
806        for item in self:
807            func(item)

Calls func on each item in the iterator.

Example
iterator = Iter([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (collections.Callable[[Item], typing.Any]): The function to call on each item in the iterator.
async def async_for_each( self, func: Callable[[~Item], Coroutine[None, typing.Any, +OtherItem]]) -> '_result.Result[collections.Sequence[OtherItem], futures.SpawnError]':
809    async def async_for_each(
810        self,
811        func: collections.Callable[
812            [Item], collections.Coroutine[None, typing.Any, OtherItem]
813        ],
814    ) -> _result.Result[collections.Sequence[OtherItem], futures.SpawnError]:
815        """Calls the async function on each item in the iterator concurrently.
816
817        Example
818        -------
819        ```py
820        async def create_user(username: str) -> None:
821            async with aiohttp.request("POST", f'.../{username}') as r:
822                return await r.json()
823
824        async def main():
825            users = sain.into_iter(["danny", "legalia"])
826            results = await users.async_for_each(lambda username: create_user(username))
827            for k, v in results.unwrap().items():
828                ...
829        ```
830
831        Parameters
832        ----------
833        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
834            The async function to call on each item in the iterator.
835        """
836        return await futures.spawn(*(func(item) for item in self))

Calls the async function on each item in the iterator concurrently.

Example
async def create_user(username: str) -> None:
    async with aiohttp.request("POST", f'.../{username}') as r:
        return await r.json()

async def main():
    users = sain.into_iter(["danny", "legalia"])
    results = await users.async_for_each(lambda username: create_user(username))
    for k, v in results.unwrap().items():
        ...
Parameters
  • func (collections.Callable[[Item], Coroutine[None, Any, Any]]): The async function to call on each item in the iterator.
@rustc_diagnostic_item('todo')
def todo(message: 'typing.LiteralString | None' = None) -> NoReturn:
365@rustc_diagnostic_item("todo")
366def todo(message: typing.LiteralString | None = None) -> typing.NoReturn:
367    """A place holder that indicates unfinished code.
368
369    Example
370    -------
371    ```py
372    from sain import todo
373
374    def from_json(payload: dict[str, int]) -> int:
375        # Calling this function will raise `Error`.
376        todo()
377    ```
378
379    Parameters
380    ----------
381    message : `str | None`
382        Multiple optional arguments to pass if the error was raised.
383    """
384    raise Error(f"not yet implemented: {message}" if message else "not yet implemented")

A place holder that indicates unfinished code.

Example
from sain import todo

def from_json(payload: dict[str, int]) -> int:
    # Calling this function will raise `Error`.
    todo()
Parameters
  • message (str | None): Multiple optional arguments to pass if the error was raised.
  • # Implementations
  • **This function implements todo:
@rustc_diagnostic_item('deprecated')
def deprecated( *, since: "typing.Literal['CURRENT_VERSION'] | typing.LiteralString | None" = None, removed_in: 'typing.LiteralString | None' = None, use_instead: 'typing.LiteralString | None' = None, hint: 'typing.LiteralString | None' = None) -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
280@rustc_diagnostic_item("deprecated")
281def deprecated(
282    *,
283    since: typing.Literal["CURRENT_VERSION"] | typing.LiteralString | None = None,
284    removed_in: typing.LiteralString | None = None,
285    use_instead: typing.LiteralString | None = None,
286    hint: typing.LiteralString | None = None,
287) -> collections.Callable[
288    [collections.Callable[P, U]],
289    collections.Callable[P, U],
290]:
291    """A decorator that marks a function as deprecated.
292
293    An attempt to call the object that's marked will cause a runtime warn.
294
295    Example
296    -------
297    ```py
298    from sain import deprecated
299
300    @deprecated(
301        since = "1.0.0",
302        removed_in ="3.0.0",
303        use_instead = "UserImpl()",
304        hint = "Hint for ux."
305    )
306    class User:
307        ...
308
309    user = User() # This will cause a warning at runtime.
310    ```
311
312    Parameters
313    ----------
314    since : `str`
315        The version that the function was deprecated. the `CURRENT_VERSION` is used internally only.
316    removed_in : `str | None`
317        If provided, It will log when will the object will be removed in.
318    use_instead : `str | None`
319        If provided, This should be the alternative object name that should be used instead.
320    hint: `str`
321        An optional hint for the user.
322    """
323
324    def _create_message(obj: typing.Any) -> str:
325        msg = f"{_obj_type(obj)} `{obj.__module__}.{obj.__name__}` is deprecated."
326
327        if since is not None:
328            if since == "CURRENT_VERSION":
329                from sain import __version__ as _version
330
331                msg += " since " + _version
332            else:
333                msg += " since " + since
334
335        if removed_in:
336            msg += f" Scheduled for removal in `{removed_in}`."
337
338        if use_instead is not None:
339            msg += f" Use `{use_instead}` instead."
340
341        if hint:
342            msg += f" Hint: {hint}"
343        return msg
344
345    def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]:
346        message = _create_message(func)
347
348        @functools.wraps(func)
349        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
350            _warn(message)
351            return func(*args, **kwargs)
352
353        # idk why pyright doesn't know the type of wrapper.
354        m = f"\n# Warning ⚠️\n{message}."
355        if wrapper.__doc__:
356            # append this message to an existing document.
357            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}"
358        else:
359            wrapper.__doc__ = m
360        return wrapper
361
362    return decorator

A decorator that marks a function as deprecated.

An attempt to call the object that's marked will cause a runtime warn.

Example
from sain import deprecated

@deprecated(
    since = "1.0.0",
    removed_in ="3.0.0",
    use_instead = "UserImpl()",
    hint = "Hint for ux."
)
class User:
    ...

user = User() # This will cause a warning at runtime.
Parameters
  • since (str): The version that the function was deprecated. the CURRENT_VERSION is used internally only.
  • removed_in (str | None): If provided, It will log when will the object will be removed in.
  • use_instead (str | None): If provided, This should be the alternative object name that should be used instead.
  • hint (str): An optional hint for the user.
  • # Implementations
  • **This function implements deprecated:
@rustc_diagnostic_item('unimplemented')
def unimplemented( *, message: 'typing.LiteralString | None' = None, available_in: 'typing.LiteralString | None' = None) -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
387@rustc_diagnostic_item("unimplemented")
388def unimplemented(
389    *,
390    message: typing.LiteralString | None = None,
391    available_in: typing.LiteralString | None = None,
392) -> collections.Callable[
393    [collections.Callable[P, U]],
394    collections.Callable[P, U],
395]:
396    """A decorator that marks an object as unimplemented.
397
398    An attempt to call the object that's marked will cause a runtime warn.
399
400    Example
401    -------
402    ```py
403    from sain import unimplemented
404
405    @unimplemented("User object is not implemented yet.")
406    class User:
407        ...
408    ```
409
410    Parameters
411    ----------
412    message : `str | None`
413        An optional message to be displayed when the function is called. Otherwise default message will be used.
414    available_in : `str | None`
415        If provided, This will be shown as what release this object be implemented.
416    """
417
418    def _create_message(obj: typing.Any) -> str:
419        msg = (
420            message
421            or f"{_obj_type(obj)} `{obj.__module__}.{obj.__name__}` is not yet implemented."
422        )  # noqa: W503
423
424        if available_in:
425            msg += f" Available in `{available_in}`."
426        return msg
427
428    def decorator(obj: collections.Callable[P, U]) -> collections.Callable[P, U]:
429        message = _create_message(obj)
430
431        @functools.wraps(obj)
432        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
433            _warn(message)
434            return obj(*args, **kwargs)
435
436        # idk why pyright doesn't know the type of wrapper.
437        m = f"\n# Warning ⚠️\n{message}."
438        if wrapper.__doc__:
439            # append this message to an existing document.
440            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}"
441        else:
442            wrapper.__doc__ = m
443        return wrapper
444
445    return decorator

A decorator that marks an object as unimplemented.

An attempt to call the object that's marked will cause a runtime warn.

Example
from sain import unimplemented

@unimplemented("User object is not implemented yet.")
class User:
    ...
Parameters
  • message (str | None): An optional message to be displayed when the function is called. Otherwise default message will be used.
  • available_in (str | None): If provided, This will be shown as what release this object be implemented.
  • # Implementations
  • **This function implements unimplemented:
@rustc_diagnostic_item('doc')
def doc( path: 'Read') -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
448@rustc_diagnostic_item("doc")
449def doc(
450    path: Read,
451) -> collections.Callable[
452    [collections.Callable[P, U]],
453    collections.Callable[P, U],
454]:
455    """Set `path` to be the object's documentation.
456
457    Example
458    -------
459    ```py
460    from sain import doc
461    from pathlib import Path
462
463    @doc(Path("../README.md"))
464    class User:
465
466        @doc("bool.html")
467        def bool_docs() -> None:
468            ...
469    ```
470
471    Parameters
472    ----------
473    path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]`
474        The path to read the content from.
475    """
476
477    def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]:
478        with open(path, "r") as file:
479            f.__doc__ = file.read()
480
481        @functools.wraps(f)
482        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
483            return f(*args, **kwargs)
484
485        return wrapper
486
487    return decorator

Set path to be the object's documentation.

Example
from sain import doc
from pathlib import Path

@doc(Path("../README.md"))
class User:

    @doc("bool.html")
    def bool_docs() -> None:
        ...
Parameters
  • path (type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]): The path to read the content from.
  • # Implementations
  • **This function implements doc:
@typing.final
@dataclasses.dataclass(slots=True, frozen=True, repr=False)
class Ok(typing.Generic[~T]):
119@typing.final
120@dataclasses.dataclass(slots=True, frozen=True, repr=False)
121class Ok(typing.Generic[T]):
122    """Contains the success value of `Result[T, ...]`."""
123
124    _inner: T
125
126    ###############################
127    # * Boolean operations. * #
128    ###############################
129
130    def is_ok(self) -> typing.Literal[True]:
131        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
132
133        Example
134        -------
135        ```py
136        value: Result[str, None] = Ok("value")
137        assert value.is_ok() == True
138        ```
139        """
140        return True
141
142    def is_ok_and(self, f: F[T, bool]) -> bool:
143        """Returns `True` if the contained value is `Ok` and `f()` returns True..
144
145        Example
146        -------
147        ```py
148        value: Result[str, None] = Ok("value")
149
150        assert value.is_ok_and(lambda inner: inner == "value")
151        # True
152        ```
153        """
154        return f(self._inner)
155
156    # These are never truthy in an `Ok` instance.
157    def is_err(self) -> typing.Literal[False]:
158        """Returns `True` if the contained value is `Err`.
159
160        Example
161        -------
162        ```py
163        value: Result[str, None] = Ok("value")
164
165        assert value.is_err() == False
166        ```
167        """
168        return False
169
170    def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]:
171        """Returns `True` if the contained value is `Ok` and `f()` returns True..
172
173        Example
174        -------
175        ```py
176        value: Result[str, None] = Ok("value")
177
178        assert value.is_err_and(lambda inner: inner == "value")
179        # False
180        ```
181        """
182        return False
183
184    ###################
185    # * Extractors. * #
186    ###################
187
188    def expect(self, message: str, /) -> T:
189        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
190        if it was `Err` with `message` passed to it.
191
192        Example
193        -------
194        ```py
195        ok: Result[str, None] = Ok("owo")
196        ok.expect("err") # owo
197
198        err: Result[str, None] = Err(None)
199        err.expect("err") # RuntimeError("err")
200        ```
201        """
202        return self._inner
203
204    def expect_err(self) -> typing.NoReturn:
205        """Return the `Err` value if `self` is an `Err`, panicking otherwise.
206
207        Example
208        -------
209        ```py
210        ok: Result[str, None] = Ok("owo")
211        ok.expect_err()  # RuntimeError("Called expect_err on `Ok`)
212
213        err: Result[str, None] = Err(None)
214        err.expect_err() # None
215        ```
216        """
217        raise RuntimeError("Called `expect_err` on an `Ok` value.")
218
219    def unwrap(self) -> T:
220        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
221
222        Example
223        -------
224        ```py
225        ok: Result[str, None] = Ok("owo")
226        ok.unwrap() # owo
227
228        err: Result[str, None] = Err(None)
229        err.unwrap() # RuntimeError
230        ```
231        """
232        return self._inner
233
234    def unwrap_or(self, default: T, /) -> T:
235        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
236
237        Example
238        -------
239        ```py
240        ok: Result[str, None] = Ok("OwO")
241        ok.unwrap_or("uwu") # OwO
242
243        err: Result[str, None] = Err(None)
244        err.unwrap_or("uwu") # uwu
245        ```
246        """
247        return self._inner
248
249    def unwrap_or_else(self, f: F[E, T]) -> T:
250        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
251
252        Example
253        -------
254        ```py
255        ok: Result[int, str] = Ok(4)
256        ok.unwrap_or_else(lambda e: 0) # 4
257
258        err: Result[int, str] = Err("word")
259        err.unwrap_or_else(lambda e: len(e)) # 4
260        ```
261        """
262        return self._inner
263
264    def unwrap_err(self) -> typing.NoReturn:
265        """Return the contained `Err` value, Raising if it was `Ok`.
266
267        Example
268        -------
269        ```py
270        ok: Result[str, None] = Ok("buh")
271        ok.unwrap_err()  # RuntimeError
272
273        err: Result[str, None] = Err(None)
274        err.unwrap_err() == None
275        # True
276        ```
277        """
278        raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}")
279
280    ############################
281    # * Conversion adapters. * #
282    ############################
283
284    def ok(self) -> Option[T]:
285        """Convert `Ok[T]` to `Option[T]` if the contained value was `Ok` and `Option[None]` if it was `Err`.
286
287        Example
288        -------
289        ```py
290        value: Result[str, None] = Ok("buh")
291        value.ok().is_some() # True
292
293        value: Result[str, int] = Err(0)
294        value.ok().is_none() # True
295        ```
296        """
297        return _option.Some(self._inner)
298
299    def err(self) -> Option[T]:
300        """Convert `Err[T]` to `Option[T]` if the contained value was `Err` and `Option[None]` if it was `Ok`.
301
302        Example
303        -------
304        ```py
305        value: Result[str, None] = Ok("buh")
306        value.err().is_none() # True
307
308        value: Result[str, int] = Err(0)
309        value.err().is_some() # True
310        ```
311        """
312        return _option.NOTHING  # pyright: ignore
313
314    def inspect(self, f: F[T, typing.Any]) -> None:
315        """Call a function to the contained value if it was `Ok` and do nothing if it was `Err`
316
317        Example
318        -------
319        ```py
320        def sink(value: str) -> None:
321            # do something with value
322            print("Called " + value)
323
324        x: Result[str, None] = Ok("ok")
325        x.inspect(sink) # "Called ok"
326
327        x: Result[str, str] = Err("err")
328        x.inspect(sink) # None
329        ```
330        """
331        f(self._inner)
332
333    def inspect_err(self, f: F[E, typing.Any]) -> None:
334        """Call a function to the contained value if it was `Err` and do nothing if it was `Ok`
335
336        Example
337        -------
338        ```py
339        def sink(value: str) -> None:
340            # do something with value
341            print("Called " + value)
342
343        x: Result[str, None] = Ok("ok")
344        x.inspect_err(sink) # None
345
346        x: Result[str, str] = Err("err")
347        x.inspect_err(sink) # Called err
348        ```
349        """
350
351    def map(self, f: F[T, U], /) -> Ok[U]:
352        """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value,
353        leaving `Err` untouched.
354
355        Example
356        -------
357        ```py
358        ok: Result[str, int] = Ok("1")
359        ok.map(lambda c: int(c) + 1) # Ok(2)
360
361        err: Result[str, int] = Err(0)
362        err.map(str.upper) # Err(0)
363        ```
364        """
365        return Ok(f(self._inner))
366
367    def map_or(self, f: F[T, U], default: U, /) -> U:
368        """Returns the provided `default` if the contained value is `Err`,
369
370        Otherwise extracts the `Ok` value and maps it to `f()`
371
372        Example
373        -------
374        ```py
375        x: Result[str, str] = Ok("foo")
376        assert x.map_or(lambda c: len(c), 42) == 3
377
378        x: Result[str, str] = Err("bar")
379        assert x.map_or(lambda c: len(c), 42) == 42
380        ```
381        """
382        return f(self._inner)
383
384    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
385        """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value,
386        or function `f` to a contained Ok value.
387
388        Example
389        -------
390        ```py
391        x: Result[str, str] = Ok("four")
392        assert x.map_or_else(
393            lambda ok: 2 * len(ok),
394            default=lambda err: len(err)
395        ) == 8
396
397        x: Result[str, str] = Err("bar")
398        assert x.map_or_else(
399            lambda c: 2 * len(c),
400            lambda err: len(err)
401        ) == 3
402        ```
403        """
404        return f(self._inner)
405
406    def map_err(self, f: F[E, U], /) -> Self:
407        """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched.
408
409        Example
410        -------
411        ```py
412        x: Result[str, int] = Ok("blue")
413        x.map_err(lambda err: err + 1) # Ok("blue")
414
415        x: Result[str, int] = Err(5)
416        x.map_err(float) # Err(5.0)
417        ```
418        """
419        return self
420
421    ##############################
422    # * Iterator constructors. * #
423    ##############################
424
425    def iter(self) -> _iter.Iterator[T]:
426        """An iterator over the possible contained value.
427
428        If `self` was `Ok`, then the iterator will yield the Ok `T`. otherwise yields nothing.
429
430        Example
431        -------
432        ```py
433        c: Result[str, int] = Ok("blue")
434        c.iter().next() == Some("blue")
435
436        c: Result[str, int] = Err(0)
437        c.iter().next() == Some(None)
438        ```
439        """
440        return self.__iter__()
441
442    def __iter__(self) -> _iter.Iterator[T]:
443        return _iter.once(self._inner)
444
445    #################
446    # * Overloads * #
447    #################
448
449    def __repr__(self) -> str:
450        return f"Ok({self._inner!r})"
451
452    def __or__(self, other: T) -> T:
453        return self._inner
454
455    def __invert__(self) -> T:
456        return self._inner

Contains the success value of Result[T, ...].

Ok(_inner: ~T)
def is_ok(self) -> Literal[True]:
130    def is_ok(self) -> typing.Literal[True]:
131        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
132
133        Example
134        -------
135        ```py
136        value: Result[str, None] = Ok("value")
137        assert value.is_ok() == True
138        ```
139        """
140        return True

Returns True if the contained value is Ok and False if it an Err.

Example
value: Result[str, None] = Ok("value")
assert value.is_ok() == True
def is_ok_and(self, f: Callable[[~T], bool]) -> bool:
142    def is_ok_and(self, f: F[T, bool]) -> bool:
143        """Returns `True` if the contained value is `Ok` and `f()` returns True..
144
145        Example
146        -------
147        ```py
148        value: Result[str, None] = Ok("value")
149
150        assert value.is_ok_and(lambda inner: inner == "value")
151        # True
152        ```
153        """
154        return f(self._inner)

Returns True if the contained value is Ok and f() returns True..

Example
value: Result[str, None] = Ok("value")

assert value.is_ok_and(lambda inner: inner == "value")
# True
def is_err(self) -> Literal[False]:
157    def is_err(self) -> typing.Literal[False]:
158        """Returns `True` if the contained value is `Err`.
159
160        Example
161        -------
162        ```py
163        value: Result[str, None] = Ok("value")
164
165        assert value.is_err() == False
166        ```
167        """
168        return False

Returns True if the contained value is Err.

Example
value: Result[str, None] = Ok("value")

assert value.is_err() == False
def is_err_and(self, f: Callable[[~T], bool]) -> Literal[False]:
170    def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]:
171        """Returns `True` if the contained value is `Ok` and `f()` returns True..
172
173        Example
174        -------
175        ```py
176        value: Result[str, None] = Ok("value")
177
178        assert value.is_err_and(lambda inner: inner == "value")
179        # False
180        ```
181        """
182        return False

Returns True if the contained value is Ok and f() returns True..

Example
value: Result[str, None] = Ok("value")

assert value.is_err_and(lambda inner: inner == "value")
# False
def expect(self, message: str, /) -> ~T:
188    def expect(self, message: str, /) -> T:
189        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
190        if it was `Err` with `message` passed to it.
191
192        Example
193        -------
194        ```py
195        ok: Result[str, None] = Ok("owo")
196        ok.expect("err") # owo
197
198        err: Result[str, None] = Err(None)
199        err.expect("err") # RuntimeError("err")
200        ```
201        """
202        return self._inner

Return the underlying value if it was Ok, Raising RuntimeError if it was Err with message passed to it.

Example
ok: Result[str, None] = Ok("owo")
ok.expect("err") # owo

err: Result[str, None] = Err(None)
err.expect("err") # RuntimeError("err")
def expect_err(self) -> NoReturn:
204    def expect_err(self) -> typing.NoReturn:
205        """Return the `Err` value if `self` is an `Err`, panicking otherwise.
206
207        Example
208        -------
209        ```py
210        ok: Result[str, None] = Ok("owo")
211        ok.expect_err()  # RuntimeError("Called expect_err on `Ok`)
212
213        err: Result[str, None] = Err(None)
214        err.expect_err() # None
215        ```
216        """
217        raise RuntimeError("Called `expect_err` on an `Ok` value.")

Return the Err value if self is an Err, panicking otherwise.

Example
ok: Result[str, None] = Ok("owo")
ok.expect_err()  # RuntimeError("Called expect_err on `Ok`)

err: Result[str, None] = Err(None)
err.expect_err() # None
def unwrap(self) -> ~T:
219    def unwrap(self) -> T:
220        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
221
222        Example
223        -------
224        ```py
225        ok: Result[str, None] = Ok("owo")
226        ok.unwrap() # owo
227
228        err: Result[str, None] = Err(None)
229        err.unwrap() # RuntimeError
230        ```
231        """
232        return self._inner

Return the underlying value if it was Ok, Raising RuntimeError if it was Err.

Example
ok: Result[str, None] = Ok("owo")
ok.unwrap() # owo

err: Result[str, None] = Err(None)
err.unwrap() # RuntimeError
def unwrap_or(self, default: ~T, /) -> ~T:
234    def unwrap_or(self, default: T, /) -> T:
235        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
236
237        Example
238        -------
239        ```py
240        ok: Result[str, None] = Ok("OwO")
241        ok.unwrap_or("uwu") # OwO
242
243        err: Result[str, None] = Err(None)
244        err.unwrap_or("uwu") # uwu
245        ```
246        """
247        return self._inner

Return the underlying value if it was Ok, returning default if it was Err.

Example
ok: Result[str, None] = Ok("OwO")
ok.unwrap_or("uwu") # OwO

err: Result[str, None] = Err(None)
err.unwrap_or("uwu") # uwu
def unwrap_or_else(self, f: Callable[[~E], ~T]) -> ~T:
249    def unwrap_or_else(self, f: F[E, T]) -> T:
250        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
251
252        Example
253        -------
254        ```py
255        ok: Result[int, str] = Ok(4)
256        ok.unwrap_or_else(lambda e: 0) # 4
257
258        err: Result[int, str] = Err("word")
259        err.unwrap_or_else(lambda e: len(e)) # 4
260        ```
261        """
262        return self._inner

Return the contained Ok value or computes it from f() if it was Err.

Example
ok: Result[int, str] = Ok(4)
ok.unwrap_or_else(lambda e: 0) # 4

err: Result[int, str] = Err("word")
err.unwrap_or_else(lambda e: len(e)) # 4
def unwrap_err(self) -> NoReturn:
264    def unwrap_err(self) -> typing.NoReturn:
265        """Return the contained `Err` value, Raising if it was `Ok`.
266
267        Example
268        -------
269        ```py
270        ok: Result[str, None] = Ok("buh")
271        ok.unwrap_err()  # RuntimeError
272
273        err: Result[str, None] = Err(None)
274        err.unwrap_err() == None
275        # True
276        ```
277        """
278        raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}")

Return the contained Err value, Raising if it was Ok.

Example
ok: Result[str, None] = Ok("buh")
ok.unwrap_err()  # RuntimeError

err: Result[str, None] = Err(None)
err.unwrap_err() == None
# True
def ok(self) -> 'Option[T]':
284    def ok(self) -> Option[T]:
285        """Convert `Ok[T]` to `Option[T]` if the contained value was `Ok` and `Option[None]` if it was `Err`.
286
287        Example
288        -------
289        ```py
290        value: Result[str, None] = Ok("buh")
291        value.ok().is_some() # True
292
293        value: Result[str, int] = Err(0)
294        value.ok().is_none() # True
295        ```
296        """
297        return _option.Some(self._inner)

Convert Ok[T] to Option[T] if the contained value was Ok and Option[None] if it was Err.

Example
value: Result[str, None] = Ok("buh")
value.ok().is_some() # True

value: Result[str, int] = Err(0)
value.ok().is_none() # True
def err(self) -> 'Option[T]':
299    def err(self) -> Option[T]:
300        """Convert `Err[T]` to `Option[T]` if the contained value was `Err` and `Option[None]` if it was `Ok`.
301
302        Example
303        -------
304        ```py
305        value: Result[str, None] = Ok("buh")
306        value.err().is_none() # True
307
308        value: Result[str, int] = Err(0)
309        value.err().is_some() # True
310        ```
311        """
312        return _option.NOTHING  # pyright: ignore

Convert Err[T] to Option[T] if the contained value was Err and Option[None] if it was Ok.

Example
value: Result[str, None] = Ok("buh")
value.err().is_none() # True

value: Result[str, int] = Err(0)
value.err().is_some() # True
def inspect(self, f: Callable[[~T], typing.Any]) -> None:
314    def inspect(self, f: F[T, typing.Any]) -> None:
315        """Call a function to the contained value if it was `Ok` and do nothing if it was `Err`
316
317        Example
318        -------
319        ```py
320        def sink(value: str) -> None:
321            # do something with value
322            print("Called " + value)
323
324        x: Result[str, None] = Ok("ok")
325        x.inspect(sink) # "Called ok"
326
327        x: Result[str, str] = Err("err")
328        x.inspect(sink) # None
329        ```
330        """
331        f(self._inner)

Call a function to the contained value if it was Ok and do nothing if it was Err

Example
def sink(value: str) -> None:
    # do something with value
    print("Called " + value)

x: Result[str, None] = Ok("ok")
x.inspect(sink) # "Called ok"

x: Result[str, str] = Err("err")
x.inspect(sink) # None
def inspect_err(self, f: Callable[[~E], typing.Any]) -> None:
333    def inspect_err(self, f: F[E, typing.Any]) -> None:
334        """Call a function to the contained value if it was `Err` and do nothing if it was `Ok`
335
336        Example
337        -------
338        ```py
339        def sink(value: str) -> None:
340            # do something with value
341            print("Called " + value)
342
343        x: Result[str, None] = Ok("ok")
344        x.inspect_err(sink) # None
345
346        x: Result[str, str] = Err("err")
347        x.inspect_err(sink) # Called err
348        ```
349        """

Call a function to the contained value if it was Err and do nothing if it was Ok

Example
def sink(value: str) -> None:
    # do something with value
    print("Called " + value)

x: Result[str, None] = Ok("ok")
x.inspect_err(sink) # None

x: Result[str, str] = Err("err")
x.inspect_err(sink) # Called err
def map(self, f: Callable[[~T], ~U], /) -> Ok[~U]:
351    def map(self, f: F[T, U], /) -> Ok[U]:
352        """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value,
353        leaving `Err` untouched.
354
355        Example
356        -------
357        ```py
358        ok: Result[str, int] = Ok("1")
359        ok.map(lambda c: int(c) + 1) # Ok(2)
360
361        err: Result[str, int] = Err(0)
362        err.map(str.upper) # Err(0)
363        ```
364        """
365        return Ok(f(self._inner))

Map Result<T, E> to Result<U, E> by applying a function to the Ok value, leaving Err untouched.

Example
ok: Result[str, int] = Ok("1")
ok.map(lambda c: int(c) + 1) # Ok(2)

err: Result[str, int] = Err(0)
err.map(str.upper) # Err(0)
def map_or(self, f: Callable[[~T], ~U], default: ~U, /) -> ~U:
367    def map_or(self, f: F[T, U], default: U, /) -> U:
368        """Returns the provided `default` if the contained value is `Err`,
369
370        Otherwise extracts the `Ok` value and maps it to `f()`
371
372        Example
373        -------
374        ```py
375        x: Result[str, str] = Ok("foo")
376        assert x.map_or(lambda c: len(c), 42) == 3
377
378        x: Result[str, str] = Err("bar")
379        assert x.map_or(lambda c: len(c), 42) == 42
380        ```
381        """
382        return f(self._inner)

Returns the provided default if the contained value is Err,

Otherwise extracts the Ok value and maps it to f()

Example
x: Result[str, str] = Ok("foo")
assert x.map_or(lambda c: len(c), 42) == 3

x: Result[str, str] = Err("bar")
assert x.map_or(lambda c: len(c), 42) == 42
def map_or_else(self, f: Callable[[~T], ~U], default: Callable[[~E], ~U], /) -> ~U:
384    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
385        """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value,
386        or function `f` to a contained Ok value.
387
388        Example
389        -------
390        ```py
391        x: Result[str, str] = Ok("four")
392        assert x.map_or_else(
393            lambda ok: 2 * len(ok),
394            default=lambda err: len(err)
395        ) == 8
396
397        x: Result[str, str] = Err("bar")
398        assert x.map_or_else(
399            lambda c: 2 * len(c),
400            lambda err: len(err)
401        ) == 3
402        ```
403        """
404        return f(self._inner)

Maps a Result to U by applying fallback function default to a contained Err value, or function f to a contained Ok value.

Example
x: Result[str, str] = Ok("four")
assert x.map_or_else(
    lambda ok: 2 * len(ok),
    default=lambda err: len(err)
) == 8

x: Result[str, str] = Err("bar")
assert x.map_or_else(
    lambda c: 2 * len(c),
    lambda err: len(err)
) == 3
def map_err(self, f: Callable[[~E], ~U], /) -> typing_extensions.Self:
406    def map_err(self, f: F[E, U], /) -> Self:
407        """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched.
408
409        Example
410        -------
411        ```py
412        x: Result[str, int] = Ok("blue")
413        x.map_err(lambda err: err + 1) # Ok("blue")
414
415        x: Result[str, int] = Err(5)
416        x.map_err(float) # Err(5.0)
417        ```
418        """
419        return self

Maps a Result[T, E] to Result[T, U] by applying function f, leaving the Ok value untouched.

Example
x: Result[str, int] = Ok("blue")
x.map_err(lambda err: err + 1) # Ok("blue")

x: Result[str, int] = Err(5)
x.map_err(float) # Err(5.0)
def iter(self) -> Iterator[~T]:
425    def iter(self) -> _iter.Iterator[T]:
426        """An iterator over the possible contained value.
427
428        If `self` was `Ok`, then the iterator will yield the Ok `T`. otherwise yields nothing.
429
430        Example
431        -------
432        ```py
433        c: Result[str, int] = Ok("blue")
434        c.iter().next() == Some("blue")
435
436        c: Result[str, int] = Err(0)
437        c.iter().next() == Some(None)
438        ```
439        """
440        return self.__iter__()

An iterator over the possible contained value.

If self was Ok, then the iterator will yield the Ok T. otherwise yields nothing.

Example
c: Result[str, int] = Ok("blue")
c.iter().next() == Some("blue")

c: Result[str, int] = Err(0)
c.iter().next() == Some(None)
@typing.final
@dataclasses.dataclass(slots=True, frozen=True, repr=False)
class Err(typing.Generic[~E]):
459@typing.final
460@dataclasses.dataclass(slots=True, frozen=True, repr=False)
461class Err(typing.Generic[E]):
462    """Contains the error value of `Result[..., E]`."""
463
464    _inner: E
465
466    ################################
467    # * Boolean operations. * #
468    ################################
469
470    def is_ok(self) -> typing.Literal[False]:
471        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
472
473        Example
474        -------
475        ```py
476        value: Result[str, None] = Err(None)
477
478        assert value.is_ok() == False
479        ```
480        """
481        return False
482
483    def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]:
484        """Returns `True` if the contained value is `Ok` and `f()` returns True.
485
486        Example
487        -------
488        ```py
489        value: Result[str, None] = Err(None)
490
491        assert value.is_ok_and(lambda inner: inner == "value")
492        # False
493        ```
494        """
495        return False
496
497    # These are never truthy in an `Ok` instance.
498    def is_err(self) -> typing.Literal[True]:
499        """Returns `True` if the contained value is `Err`.
500
501        Example
502        -------
503        ```py
504        value: Result[str, None] = Err(None)
505
506        assert value.is_err() == True
507        ```
508        """
509        return True
510
511    def is_err_and(self, f: F[E, bool]) -> bool:
512        """Returns `True` if the contained value is `Ok` and `f()` returns True..
513
514        Example
515        -------
516        ```py
517        value: Result[str, None] = Err(None)
518
519        assert value.is_err_and(lambda err: err is None)
520        # True
521        ```
522        """
523        return f(self._inner)
524
525    ###################
526    # * Extractors. * #
527    ###################
528
529    def expect(self, msg: str) -> typing.NoReturn:
530        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
531        if it was `Err` with `message` passed to it.
532
533        Example
534        -------
535        ```py
536        ok: Result[str, None] = Ok("owo")
537        ok.expect("err") # owo
538
539        err: Result[str, None] = Err(None)
540        err.expect("err") # RuntimeError("err")
541        ```
542        """
543        raise RuntimeError(msg) from None
544
545    def expect_err(self) -> E:
546        """Return the `Err` if it was `Err`, panicking otherwise.
547
548
549        Example
550        -------
551        ```py
552        x: Result[str, None] = Ok("owo")
553        x.expect_err()  # RuntimeError("Called expect_err on `Ok`)
554
555        x: Result[str, None] = Err(None)
556        x.expect_err() # None
557        ```
558        """
559        return self._inner
560
561    def unwrap(self) -> typing.NoReturn:
562        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
563
564        Example
565        -------
566        ```py
567        ok: Result[str, None] = Ok("owo")
568        ok.unwrap() # owo
569
570        err: Result[str, None] = Err(None)
571        err.unwrap() # RuntimeError
572        ```
573        """
574        raise RuntimeError(
575            f"Called `unwrap()` on an `Err` variant: {self._inner!r}"
576        ) from None
577
578    def unwrap_or(self, default: T, /) -> T:
579        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
580
581        Example
582        -------
583        ```py
584        ok: Result[str, None] = Ok("OwO")
585        ok.unwrap_or("uwu") # OwO
586
587        err: Result[str, None] = Err(None)
588        err.unwrap_or("uwu") # uwu
589        ```
590        """
591        return default
592
593    def unwrap_or_else(self, f: F[E, T]) -> T:
594        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
595
596        Example
597        -------
598        ```py
599        ok: Result[int, str] = Ok(4)
600        ok.unwrap_or_else(lambda e: 0) # 4
601
602        err: Result[int, str] = Err("word")
603        err.unwrap_or_else(lambda e: len(e)) # 4
604        ```
605        """
606        return f(self._inner)
607
608    def unwrap_err(self) -> E:
609        """Return the contained `Err` value, Raising if it was `Ok`.
610
611        Example
612        -------
613        ```py
614        ok: Result[str, None] = Ok("buh")
615        ok.unwrap_err()  # RuntimeError
616
617        err: Result[str, None] = Err(None)
618        err.unwrap_err() == None
619        # True
620        ```
621        """
622        return self._inner
623
624    ############################
625    # * Conversion adapters. * #
626    ############################
627
628    def inspect(self, f: F[T, typing.Any]) -> None:
629        """Call a function to the contained value if it was `Ok` and do nothing if it was `Err`
630
631        Example
632        -------
633        ```py
634        def sink(value: str) -> None:
635            # do something with value
636            print("Called " + value)
637
638        x: Result[str, None] = Ok("ok")
639        x.inspect(sink) # "Called ok"
640
641        x: Result[str, str] = Err("err")
642        x.inspect(sink) # None
643        ```
644        """
645        return None
646
647    def inspect_err(self, f: F[E, typing.Any]) -> None:
648        """Call a function to the contained value if it was `Err` and do nothing if it was `Ok`
649
650        Example
651        -------
652        ```py
653        def sink(value: str) -> None:
654            # do something with value
655            print("Called " + value)
656
657        x: Result[str, None] = Ok("ok")
658        x.inspect_err(sink) # None
659
660        x: Result[str, str] = Err("err")
661        x.inspect_err(sink) # Called err
662        ```
663        """
664        f(self._inner)
665
666    def ok(self) -> Option[None]:
667        """Convert `Ok[T]` to `Option[T]` if the contained value was `Ok` and `Option[None]` if it was `Err`.
668
669        Example
670        -------
671        ```py
672        value: Result[str, None] = Ok("buh")
673        value.ok().is_some() # True
674
675        value: Result[str, int] = Err(0)
676        value.ok().is_none() # True
677        ```
678        """
679        return _option.NOTHING
680
681    def err(self) -> Option[E]:
682        """Convert `Err[T]` to `Option[T]` if the contained value was `Err` and `Option[None]` if it was `Ok`.
683
684        Example
685        -------
686        ```py
687        value: Result[str, None] = Ok("buh")
688        value.err().is_none() # True
689
690        value: Result[str, int] = Err(0)
691        value.err().is_some() # True
692        ```
693        """
694        return _option.Some(self._inner)
695
696    def map(self, f: F[E, U]) -> Self:
697        """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value,
698        leaving `Err` untouched.
699
700        Example
701        -------
702        ```py
703        ok: Result[str, int] = Ok("1")
704        ok.map(lambda c: int(c) + 1) # Ok(2)
705
706        err: Result[str, int] = Err(0)
707        err.map(str.upper) # Err(0)
708        ```
709        """
710        return self
711
712    def map_or(self, f: F[E, U], default: U, /) -> U:
713        """Returns the provided `default` if the contained value is `Err`,
714
715        Otherwise extracts the `Ok` value and maps it to `f()`
716
717        Example
718        -------
719        ```py
720        x: Result[str, str] = Ok("foo")
721        assert x.map_or(lambda c: len(c), 42) == 3
722
723        x: Result[str, str] = Err("bar")
724        assert x.map_or(lambda c: len(c), 42) == 42
725        ```
726        """
727        return default
728
729    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
730        """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value,
731        or function `f` to a contained Ok value.
732
733        Example
734        -------
735        ```py
736        x: Result[str, str] = Ok("four")
737        assert x.map_or_else(
738            lambda ok: 2 * len(ok),
739            default=lambda err: len(err)
740        ) == 8
741
742        x: Result[str, str] = Err("bar")
743        assert x.map_or_else(
744            lambda c: 2 * len(c),
745            lambda err: len(err)
746        ) == 3
747        ```
748        """
749        return default(self._inner)
750
751    def map_err(self, f: F[E, U]) -> Err[U]:
752        """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched.
753
754        Example
755        -------
756        ```py
757        x: Result[str, int] = Ok("blue")
758        x.map_err(lambda err: err + 1) # Ok("blue")
759
760        x: Result[str, int] = Err(5)
761        x.map_err(float) # Err(5.0)
762        ```
763        """
764        return Err(f(self._inner))
765
766    ##############################
767    # * Iterator constructors. * #
768    ##############################
769
770    def iter(self) -> _iter.Iterator[Never]:
771        """An iterator over the possible contained value.
772
773        If `self` was `Ok`, then the iterator will yield `T`, otherwise yields nothing.
774
775        Example
776        -------
777        ```py
778        c: Result[str, int] = Ok("blue")
779        c.iter().next() == Some("blue")
780
781        c: Result[str, int] = Err(0)
782        c.iter().next() == Some(None)
783        ```
784        """
785        return self.__iter__()
786
787    def __iter__(self) -> _iter.Iterator[Never]:
788        return _iter.Empty()
789
790    #################
791    # * Overloads * #
792    #################
793
794    def __repr__(self) -> str:
795        return f"Err({self._inner!r})"
796
797    def __or__(self, other: T) -> T:
798        return other
799
800    def __invert__(self) -> typing.NoReturn:
801        self.unwrap()

Contains the error value of Result[..., E].

Err(_inner: ~E)
def is_ok(self) -> Literal[False]:
470    def is_ok(self) -> typing.Literal[False]:
471        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
472
473        Example
474        -------
475        ```py
476        value: Result[str, None] = Err(None)
477
478        assert value.is_ok() == False
479        ```
480        """
481        return False

Returns True if the contained value is Ok and False if it an Err.

Example
value: Result[str, None] = Err(None)

assert value.is_ok() == False
def is_ok_and(self, f: Callable[[~E], bool]) -> Literal[False]:
483    def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]:
484        """Returns `True` if the contained value is `Ok` and `f()` returns True.
485
486        Example
487        -------
488        ```py
489        value: Result[str, None] = Err(None)
490
491        assert value.is_ok_and(lambda inner: inner == "value")
492        # False
493        ```
494        """
495        return False

Returns True if the contained value is Ok and f() returns True.

Example
value: Result[str, None] = Err(None)

assert value.is_ok_and(lambda inner: inner == "value")
# False
def is_err(self) -> Literal[True]:
498    def is_err(self) -> typing.Literal[True]:
499        """Returns `True` if the contained value is `Err`.
500
501        Example
502        -------
503        ```py
504        value: Result[str, None] = Err(None)
505
506        assert value.is_err() == True
507        ```
508        """
509        return True

Returns True if the contained value is Err.

Example
value: Result[str, None] = Err(None)

assert value.is_err() == True
def is_err_and(self, f: Callable[[~E], bool]) -> bool:
511    def is_err_and(self, f: F[E, bool]) -> bool:
512        """Returns `True` if the contained value is `Ok` and `f()` returns True..
513
514        Example
515        -------
516        ```py
517        value: Result[str, None] = Err(None)
518
519        assert value.is_err_and(lambda err: err is None)
520        # True
521        ```
522        """
523        return f(self._inner)

Returns True if the contained value is Ok and f() returns True..

Example
value: Result[str, None] = Err(None)

assert value.is_err_and(lambda err: err is None)
# True
def expect(self, msg: str) -> NoReturn:
529    def expect(self, msg: str) -> typing.NoReturn:
530        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
531        if it was `Err` with `message` passed to it.
532
533        Example
534        -------
535        ```py
536        ok: Result[str, None] = Ok("owo")
537        ok.expect("err") # owo
538
539        err: Result[str, None] = Err(None)
540        err.expect("err") # RuntimeError("err")
541        ```
542        """
543        raise RuntimeError(msg) from None

Return the underlying value if it was Ok, Raising RuntimeError if it was Err with message passed to it.

Example
ok: Result[str, None] = Ok("owo")
ok.expect("err") # owo

err: Result[str, None] = Err(None)
err.expect("err") # RuntimeError("err")
def expect_err(self) -> ~E:
545    def expect_err(self) -> E:
546        """Return the `Err` if it was `Err`, panicking otherwise.
547
548
549        Example
550        -------
551        ```py
552        x: Result[str, None] = Ok("owo")
553        x.expect_err()  # RuntimeError("Called expect_err on `Ok`)
554
555        x: Result[str, None] = Err(None)
556        x.expect_err() # None
557        ```
558        """
559        return self._inner

Return the Err if it was Err, panicking otherwise.

Example
x: Result[str, None] = Ok("owo")
x.expect_err()  # RuntimeError("Called expect_err on `Ok`)

x: Result[str, None] = Err(None)
x.expect_err() # None
def unwrap(self) -> NoReturn:
561    def unwrap(self) -> typing.NoReturn:
562        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
563
564        Example
565        -------
566        ```py
567        ok: Result[str, None] = Ok("owo")
568        ok.unwrap() # owo
569
570        err: Result[str, None] = Err(None)
571        err.unwrap() # RuntimeError
572        ```
573        """
574        raise RuntimeError(
575            f"Called `unwrap()` on an `Err` variant: {self._inner!r}"
576        ) from None

Return the underlying value if it was Ok, Raising RuntimeError if it was Err.

Example
ok: Result[str, None] = Ok("owo")
ok.unwrap() # owo

err: Result[str, None] = Err(None)
err.unwrap() # RuntimeError
def unwrap_or(self, default: ~T, /) -> ~T:
578    def unwrap_or(self, default: T, /) -> T:
579        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
580
581        Example
582        -------
583        ```py
584        ok: Result[str, None] = Ok("OwO")
585        ok.unwrap_or("uwu") # OwO
586
587        err: Result[str, None] = Err(None)
588        err.unwrap_or("uwu") # uwu
589        ```
590        """
591        return default

Return the underlying value if it was Ok, returning default if it was Err.

Example
ok: Result[str, None] = Ok("OwO")
ok.unwrap_or("uwu") # OwO

err: Result[str, None] = Err(None)
err.unwrap_or("uwu") # uwu
def unwrap_or_else(self, f: Callable[[~E], ~T]) -> ~T:
593    def unwrap_or_else(self, f: F[E, T]) -> T:
594        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
595
596        Example
597        -------
598        ```py
599        ok: Result[int, str] = Ok(4)
600        ok.unwrap_or_else(lambda e: 0) # 4
601
602        err: Result[int, str] = Err("word")
603        err.unwrap_or_else(lambda e: len(e)) # 4
604        ```
605        """
606        return f(self._inner)

Return the contained Ok value or computes it from f() if it was Err.

Example
ok: Result[int, str] = Ok(4)
ok.unwrap_or_else(lambda e: 0) # 4

err: Result[int, str] = Err("word")
err.unwrap_or_else(lambda e: len(e)) # 4
def unwrap_err(self) -> ~E:
608    def unwrap_err(self) -> E:
609        """Return the contained `Err` value, Raising if it was `Ok`.
610
611        Example
612        -------
613        ```py
614        ok: Result[str, None] = Ok("buh")
615        ok.unwrap_err()  # RuntimeError
616
617        err: Result[str, None] = Err(None)
618        err.unwrap_err() == None
619        # True
620        ```
621        """
622        return self._inner

Return the contained Err value, Raising if it was Ok.

Example
ok: Result[str, None] = Ok("buh")
ok.unwrap_err()  # RuntimeError

err: Result[str, None] = Err(None)
err.unwrap_err() == None
# True
def inspect(self, f: Callable[[~T], typing.Any]) -> None:
628    def inspect(self, f: F[T, typing.Any]) -> None:
629        """Call a function to the contained value if it was `Ok` and do nothing if it was `Err`
630
631        Example
632        -------
633        ```py
634        def sink(value: str) -> None:
635            # do something with value
636            print("Called " + value)
637
638        x: Result[str, None] = Ok("ok")
639        x.inspect(sink) # "Called ok"
640
641        x: Result[str, str] = Err("err")
642        x.inspect(sink) # None
643        ```
644        """
645        return None

Call a function to the contained value if it was Ok and do nothing if it was Err

Example
def sink(value: str) -> None:
    # do something with value
    print("Called " + value)

x: Result[str, None] = Ok("ok")
x.inspect(sink) # "Called ok"

x: Result[str, str] = Err("err")
x.inspect(sink) # None
def inspect_err(self, f: Callable[[~E], typing.Any]) -> None:
647    def inspect_err(self, f: F[E, typing.Any]) -> None:
648        """Call a function to the contained value if it was `Err` and do nothing if it was `Ok`
649
650        Example
651        -------
652        ```py
653        def sink(value: str) -> None:
654            # do something with value
655            print("Called " + value)
656
657        x: Result[str, None] = Ok("ok")
658        x.inspect_err(sink) # None
659
660        x: Result[str, str] = Err("err")
661        x.inspect_err(sink) # Called err
662        ```
663        """
664        f(self._inner)

Call a function to the contained value if it was Err and do nothing if it was Ok

Example
def sink(value: str) -> None:
    # do something with value
    print("Called " + value)

x: Result[str, None] = Ok("ok")
x.inspect_err(sink) # None

x: Result[str, str] = Err("err")
x.inspect_err(sink) # Called err
def ok(self) -> 'Option[None]':
666    def ok(self) -> Option[None]:
667        """Convert `Ok[T]` to `Option[T]` if the contained value was `Ok` and `Option[None]` if it was `Err`.
668
669        Example
670        -------
671        ```py
672        value: Result[str, None] = Ok("buh")
673        value.ok().is_some() # True
674
675        value: Result[str, int] = Err(0)
676        value.ok().is_none() # True
677        ```
678        """
679        return _option.NOTHING

Convert Ok[T] to Option[T] if the contained value was Ok and Option[None] if it was Err.

Example
value: Result[str, None] = Ok("buh")
value.ok().is_some() # True

value: Result[str, int] = Err(0)
value.ok().is_none() # True
def err(self) -> 'Option[E]':
681    def err(self) -> Option[E]:
682        """Convert `Err[T]` to `Option[T]` if the contained value was `Err` and `Option[None]` if it was `Ok`.
683
684        Example
685        -------
686        ```py
687        value: Result[str, None] = Ok("buh")
688        value.err().is_none() # True
689
690        value: Result[str, int] = Err(0)
691        value.err().is_some() # True
692        ```
693        """
694        return _option.Some(self._inner)

Convert Err[T] to Option[T] if the contained value was Err and Option[None] if it was Ok.

Example
value: Result[str, None] = Ok("buh")
value.err().is_none() # True

value: Result[str, int] = Err(0)
value.err().is_some() # True
def map(self, f: Callable[[~E], ~U]) -> typing_extensions.Self:
696    def map(self, f: F[E, U]) -> Self:
697        """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value,
698        leaving `Err` untouched.
699
700        Example
701        -------
702        ```py
703        ok: Result[str, int] = Ok("1")
704        ok.map(lambda c: int(c) + 1) # Ok(2)
705
706        err: Result[str, int] = Err(0)
707        err.map(str.upper) # Err(0)
708        ```
709        """
710        return self

Map Result<T, E> to Result<U, E> by applying a function to the Ok value, leaving Err untouched.

Example
ok: Result[str, int] = Ok("1")
ok.map(lambda c: int(c) + 1) # Ok(2)

err: Result[str, int] = Err(0)
err.map(str.upper) # Err(0)
def map_or(self, f: Callable[[~E], ~U], default: ~U, /) -> ~U:
712    def map_or(self, f: F[E, U], default: U, /) -> U:
713        """Returns the provided `default` if the contained value is `Err`,
714
715        Otherwise extracts the `Ok` value and maps it to `f()`
716
717        Example
718        -------
719        ```py
720        x: Result[str, str] = Ok("foo")
721        assert x.map_or(lambda c: len(c), 42) == 3
722
723        x: Result[str, str] = Err("bar")
724        assert x.map_or(lambda c: len(c), 42) == 42
725        ```
726        """
727        return default

Returns the provided default if the contained value is Err,

Otherwise extracts the Ok value and maps it to f()

Example
x: Result[str, str] = Ok("foo")
assert x.map_or(lambda c: len(c), 42) == 3

x: Result[str, str] = Err("bar")
assert x.map_or(lambda c: len(c), 42) == 42
def map_or_else(self, f: Callable[[~T], ~U], default: Callable[[~E], ~U], /) -> ~U:
729    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
730        """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value,
731        or function `f` to a contained Ok value.
732
733        Example
734        -------
735        ```py
736        x: Result[str, str] = Ok("four")
737        assert x.map_or_else(
738            lambda ok: 2 * len(ok),
739            default=lambda err: len(err)
740        ) == 8
741
742        x: Result[str, str] = Err("bar")
743        assert x.map_or_else(
744            lambda c: 2 * len(c),
745            lambda err: len(err)
746        ) == 3
747        ```
748        """
749        return default(self._inner)

Maps a Result to U by applying fallback function default to a contained Err value, or function f to a contained Ok value.

Example
x: Result[str, str] = Ok("four")
assert x.map_or_else(
    lambda ok: 2 * len(ok),
    default=lambda err: len(err)
) == 8

x: Result[str, str] = Err("bar")
assert x.map_or_else(
    lambda c: 2 * len(c),
    lambda err: len(err)
) == 3
def map_err(self, f: Callable[[~E], ~U]) -> Err[~U]:
751    def map_err(self, f: F[E, U]) -> Err[U]:
752        """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched.
753
754        Example
755        -------
756        ```py
757        x: Result[str, int] = Ok("blue")
758        x.map_err(lambda err: err + 1) # Ok("blue")
759
760        x: Result[str, int] = Err(5)
761        x.map_err(float) # Err(5.0)
762        ```
763        """
764        return Err(f(self._inner))

Maps a Result[T, E] to Result[T, U] by applying function f, leaving the Ok value untouched.

Example
x: Result[str, int] = Ok("blue")
x.map_err(lambda err: err + 1) # Ok("blue")

x: Result[str, int] = Err(5)
x.map_err(float) # Err(5.0)
def iter(self) -> Iterator[typing_extensions.Never]:
770    def iter(self) -> _iter.Iterator[Never]:
771        """An iterator over the possible contained value.
772
773        If `self` was `Ok`, then the iterator will yield `T`, otherwise yields nothing.
774
775        Example
776        -------
777        ```py
778        c: Result[str, int] = Ok("blue")
779        c.iter().next() == Some("blue")
780
781        c: Result[str, int] = Err(0)
782        c.iter().next() == Some(None)
783        ```
784        """
785        return self.__iter__()

An iterator over the possible contained value.

If self was Ok, then the iterator will yield T, otherwise yields nothing.

Example
c: Result[str, int] = Ok("blue")
c.iter().next() == Some("blue")

c: Result[str, int] = Err(0)
c.iter().next() == Some(None)
Result = 'Ok[T] | Err[E]'
@typing.final
class Vec(typing.Generic[~T]):
 70@typing.final
 71class Vec(typing.Generic[T]):
 72    """A contiguous growable alternative to builtin `list` with extra functionalities.
 73
 74    The layout of `Vec` is almost the same as `list`.
 75
 76    When initializing a vec, it will not build the underlying list until the first element gets pushed.
 77    Which saves a little bit of memory.
 78
 79    Example
 80    -------
 81    ```py
 82    names = Vec()
 83    names.push('foo')
 84    names.push('bar')
 85
 86    print(names) # ['foo', 'bar']
 87    assert names.len() == 2
 88    ```
 89
 90    Constructing
 91    ------------
 92    * `Vec()`: Create an unallocated vec, Which means the underlying list will be `None` until you start pushing into it
 93    * `Vec(other_list)`: Create a vec which points to `other_list`
 94    * `Vec((1, 2, 3))`: Create a vec with `[1, 2, 3]` pre-allocated
 95    * `Vec.with_capacity(5)`: Create a vec that can hold up to 5 elements
 96    * `from_args(1, 2, 3)`: Create a vec from arguments. This is not a classmethod
 97
 98    Iterating over `Vec`
 99    -------------------
100    There're two ways to iterate over a `Vec`. The first is to normally use `for` loop.
101
102    ```py
103    for i in names:
104        print(i)
105
106    # foo
107    # bar
108    ```
109
110    The second is to use `Vec.iter`, which yields all items in this `Vec` from start to end.
111    Then the iterator gets exhausted as usual, See `sain.Iterator`.
112
113    ```py
114    iterator = names.iter()
115    for name in iterator.map(str.upper):
116        print(name)
117
118    # FOO
119    # BAR
120
121    # No more items, The actual vec is left unchanged.
122    assert iterator.next().is_none()
123    ```
124
125    ## Comparison operators
126    A `Vec` may be compared with another `Vec` or a `list`, which is what this type is built on-top of.
127    any other type will return `False`
128
129    ```py
130    vec = Vec([1, 2, 3])
131    assert vec == [1, 2, 3] # True
132    assert not vec == (1, 2, 3)
133    ```
134
135    Zero-Copy
136    ---------
137    A vec that gets initialized from a `list` will *point* to it and doesn't copy it.
138    So any element that gets appended to the list will also get pushed into the vec
139    thats pointing to it.
140
141    ```py
142    cells: list[str] = []
143    vec = Vec(cells) # This DOES NOT copy the `cells`.
144
145    cells.append("foo")
146    vec[0] == "foo"  # True
147    ```
148
149    The opposite of the above is to initialize the vec from either
150    an iterable or args, or copy the list.
151
152    ```py
153    from sain.collections import vec, Vec
154
155    # Creates a new vec and extend it with the elements.
156    from_args = vec.from_args("foo", "bar")
157
158    # inlined from another iterable.
159    from_iter = Vec(["foo", "bar"])
160
161    # Copy the list into a vec.
162    vec = Vec(cells[:])
163    cells.append("bar")
164
165    vec[1] # IndexError: "bar" doesn't exist in vec.
166    ```
167    """
168
169    __slots__ = ("_ptr", "_capacity")
170
171    @typing.overload
172    def __init__(self) -> None: ...
173
174    @typing.overload
175    def __init__(self, iterable: collections.Iterable[T]) -> None: ...
176
177    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
178        # We won't allocate to build the list here.
179        # Instead, On first push or fist indexed set
180        # we allocate if it was None.
181        if isinstance(iterable, list):
182            # Calling `list()` on another list will copy it, So instead we just point to it.
183            self._ptr = iterable
184        elif isinstance(iterable, Vec):
185            self._ptr = iterable._ptr
186        # any other iterable that ain't a list needs to get copied into a new list.
187        else:
188            self._ptr: list[T] | None = list(iterable) if iterable else None
189
190        self._capacity: int | None = None
191
192    @classmethod
193    def with_capacity(cls, capacity: int) -> Vec[T]:
194        """Create a new `Vec` with at least the specified capacity.
195        This vec will be able to hold `capacity` elements without pushing further.
196
197        Check out `Vec.push_within_capacity` as well.
198
199        Example
200        -------
201        ```py
202        vec = Vec.with_capacity(3)
203        assert vec.len() == 0 and vec.capacity() >= 3
204
205        vec.push(1)
206        vec.push(2)
207        vec.push(3)
208        print(vec.len()) # 3
209
210        # This won't push.
211        vec.push(4)
212        ```
213        """
214        v = cls()
215        v._capacity = capacity
216        return v
217
218    def as_ref(self) -> collections.Collection[T]:
219        """Return an immutable view over this vector elements.
220
221        This will *copy* `self` elements into a new tuple.
222
223        Example
224        -------
225        ```py
226        vec = Vec((1,2,3))
227        assert vec.as_ref() == (1, 2, 3)
228        ```
229        """
230        return tuple(self)
231
232    def len(self) -> int:
233        """Return the number of elements in this vector.
234
235        Example
236        -------
237        ```py
238        vec = Vec((1,2,3))
239
240        assert vec.len() == 3
241        ```
242        """
243        return self.__len__()
244
245    def capacity(self) -> int:
246        """Return the capacity of this vector if set. 0 if not .
247
248        Example
249        -------
250        ```py
251        vec_with_cap = Vec.with_capacity(3)
252        assert vec_with_cap.capacity().unwrap() == 3
253
254        vec = Vec([1, 2, 3])
255        assert vec.capacity() == 0
256        ```
257        """
258        return 0 if self._capacity is None else self._capacity
259
260    def iter(self) -> _iter.Iterator[T]:
261        """Return an iterator over this vector elements.
262
263        Example
264        -------
265        ```py
266        vec = Vec([1 ,2, 3])
267        for element in vec.iter().map(str):
268            print(element)
269        ```
270        """
271        return _iter.Iter(self)
272
273    def is_empty(self) -> bool:
274        """Returns true if the vector contains no elements."""
275        return not self._ptr
276
277    def split_off(self, at: int) -> Vec[T]:
278        """Split the vector off at the specified position, returning a new
279        vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`.
280
281        if this vec is empty, `self` is returned unchanged.
282
283        Example
284        -------
285        ```py
286        origin = Vec((1, 2, 3, 4))
287        split = vec.split_off(2)
288
289        print(origin, split)  # [1, 2], [3, 4]
290        ```
291
292        Raises
293        ------
294        `RuntimeError`
295            This method will raise if `at` > `len(self)`
296        """
297        len_ = self.len()
298        if at > len_:
299            raise RuntimeError(
300                f"Index `at` ({at}) should be <= than len of vector ({len_}) "
301            ) from None
302
303        # Either the list is empty or uninit.
304        if not self._ptr:
305            return self
306
307        split = self[at:len_]  # split the items into a new vec.
308        del self._ptr[at:len_]  # remove the items from the original list.
309        return split
310
311    def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
312        """Split the first and rest elements of the vector, If empty, `None` is returned.
313
314        Example
315        -------
316        ```py
317        vec = Vec([1, 2, 3])
318        split = vec.split_first()
319        assert split == Some((1, [2, 3]))
320
321        vec: Vec[int] = Vec()
322        split = vec.split_first()
323        assert split.is_none()
324        ```
325        """
326        if not self._ptr:
327            return _option.NOTHING  # pyright: ignore
328
329        # optimized to only one element in the vector.
330        if self.len() == 1:
331            return _option.Some((self[0], ()))
332
333        first, *rest = self._ptr
334        return _option.Some((first, rest))
335
336    def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
337        """Split the last and rest elements of the vector, If empty, `None` is returned.
338
339        Example
340        -------
341        ```py
342        vec = Vec([1, 2, 3])
343        last, rest = vec.split_last().unwrap()
344        assert (last, rest) == [3, [1, 2]]
345        ```
346        """
347        if not self._ptr:
348            return _option.NOTHING  # pyright: ignore
349
350        # optimized to only one element in the vector.
351        if self.len() == 1:
352            return _option.Some((self[0], ()))
353
354        last = self[-1]
355        return _option.Some((last, [*self[:-1]]))
356
357    def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]:
358        """Divide `self` into two at an index.
359
360        The first will contain all elements from `[0:mid]` excluding `mid` it self.
361        and the second will contain the remaninig elements.
362
363        if `mid` > `self.len()`, Then all elements will be moved to the left,
364        returning an empty vec in right.
365
366        Example
367        -------
368        ```py
369        buffer = Vec((1, 2, 3, 4))
370        left, right = buffer.split_at(0)
371        assert left == [] and right == [1, 2, 3, 4]
372
373        left, right = buffer.split_at(2)
374        assert left == [1, 2] and right == [2, 3]
375        ```
376
377        The is roughly the implementation
378        ```py
379        self[0:mid], self[mid:]
380        ```
381        """
382        return self[0:mid], self[mid:]
383
384    def swap(self, a: int, b: int):
385        """Swap two elements in the vec.
386
387        if `a` equals to `b` then it's guaranteed that elements won't change value.
388
389        Example
390        -------
391        ```py
392        buf = Vec([1, 2, 3, 4])
393        buf.swap(0, 3)
394        assert buf == [4, 2, 3, 1]
395        ```
396
397        Raises
398        ------
399        IndexError
400            If the positions of `a` or `b` are out of index.
401        """
402        if self[a] == self[b]:
403            return
404
405        self[a], self[b] = self[b], self[a]
406
407    def swap_unchecked(self, a: int, b: int):
408        """Swap two elements in the vec. without checking if `a` == `b`.
409
410        If you care about `a` and `b` equality, see `Vec.swap`.
411
412        Example
413        -------
414        ```py
415        buf = Vec([1, 2, 3, 1])
416        buf.swap_unchecked(0, 3)
417        assert buf == [1, 2, 3, 1]
418        ```
419
420        Raises
421        ------
422        IndexError
423            If the positions of `a` or `b` are out of index.
424        """
425        self[a], self[b] = self[b], self[a]
426
427    def first(self) -> _option.Option[T]:
428        """Get the first element in this vec, returning `None` if there's none.
429
430        Example
431        -------
432        ```py
433        vec = Vec((1,2,3))
434        first = vec.first()
435        assert ~first == 1
436        ```
437        """
438        return self.get(0)
439
440    def last(self) -> _option.Option[T]:
441        """Get the last element in this vec, returning `None` if there's none.
442
443        Example
444        -------
445        ```py
446        vec = Vec([1, 2, 3, 4])
447        first = vec.last()
448        assert ~first == 4
449        ```
450        """
451        return self.get(-1)
452
453    def truncate(self, size: int) -> None:
454        """Shortens the vec, keeping the first `size` elements and dropping the rest.
455
456        Example
457        -------
458        ```py
459        vec = Vec([1,2,3])
460        vec.truncate(1)
461        assert vec == [1]
462        ```
463        """
464        if not self._ptr:
465            return
466
467        del self._ptr[size:]
468
469    def retain(self, f: collections.Callable[[T], bool]) -> None:
470        """Remove elements from this vec in-place while `f()` returns `True`.
471
472        In other words, filter this vector based on `f()`.
473
474        Example
475        -------
476        ```py
477        vec = Vec([1, 2, 3])
478        vec.retain(lambda elem: elem > 1)
479
480        assert vec == [2, 3]
481        ```
482        """
483        if not self._ptr:
484            return
485
486        for idx, e in enumerate(self._ptr):
487            if f(e):
488                del self._ptr[idx]
489
490    def swap_remove(self, item: T) -> T:
491        """Remove the first appearance of `item` from this vector and return it.
492
493        Raises
494        ------
495        * `ValueError`: if `item` is not in this vector.
496        * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it.
497
498        Example
499        -------
500        ```py
501        vec = Vec(('a', 'b', 'c'))
502        element = vec.remove('a')
503        assert vec == ['b', 'c'] and element == 'a'
504        ```
505        """
506        if self._ptr is None:
507            raise MemoryError("Vec is unallocated.") from None
508
509        return self._ptr.pop(self.index(item))
510
511    def fill(self, value: T) -> None:
512        """Fill `self` with the given `value`.
513
514        Nothing happens if the vec is empty or unallocated.
515
516        Example
517        ```py
518        a = Vec([0, 1, 2, 3])
519        a.fill(0)
520        assert a == [0, 0, 0, 0]
521        ```
522        """
523        if not self._ptr:
524            return
525
526        for n, _ in enumerate(self):
527            self[n] = value
528
529    def push(self, item: T) -> None:
530        """Push an element at the end of the vector.
531
532        Example
533        -------
534        ```py
535        vec = Vec()
536        vec.push(1)
537
538        assert vec == [1]
539        ```
540        """
541        if self._capacity is not None:
542            self.push_within_capacity(item)
543            return
544
545        if self._ptr is None:
546            self._ptr = []
547
548        self._ptr.append(item)
549
550    def push_within_capacity(self, x: T) -> Result[None, T]:
551        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
552
553        Example
554        -------
555        ```py
556        vec: Vec[int] = Vec.with_capacity(3)
557        for i in range(3):
558            match vec.push_within_capacity(i):
559                case Ok(_):
560                    print("All good.")
561                case Err(elem):
562                    print("Reached max cap :< cant push", elem)
563        ```
564
565        Or you can also just call `Vec.push` and it will push if theres is sufficient capacity.
566        ```py
567        vec: Vec[int] = Vec.with_capacity(3)
568
569        vec.extend((1, 2, 3))
570        vec.push(4)
571
572        assert vec.len() == 3
573        ```
574        """
575        if self._ptr is None:
576            self._ptr = []
577
578        if self.len() == self._capacity:
579            return _result.Err(x)
580
581        self._ptr.append(x)
582        return _result.Ok(None)
583
584    def reserve(self, additional: int) -> None:
585        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
586
587        Example
588        -------
589        ```py
590        vec = Vec.with_capacity(3)
591        is_vip = random.choice((True, False))
592
593        for i in range(4):
594            match vec.push_within_capacity(i):
595                case Ok(_):
596                    print("All good")
597                case Err(person):
598                    # If the person is a VIP, then reserve for one more.
599                    if is_vip:
600                        vec.reserve(1)
601                        continue
602
603                    # is_vip was false.
604                    print("Can't reserve for non-VIP members...", person)
605                    break
606        ```
607        """
608        if self._capacity is not None:
609            self._capacity += additional
610
611    ##########################
612    # * Builtin Operations *
613    ##########################
614
615    # For people how are used to calling list.append
616    append = push
617    """An alias for `Vec.push` method."""
618
619    def get(self, index: int) -> _option.Option[T]:
620        """Get the item at the given index, or `Some[None]` if its out of bounds.
621
622        Example
623        -------
624        ```py
625        vec = Vec((1, 2, 3))
626        vec.get(0) == Some(1)
627        vec.get(3) == Some(None)
628        ```
629        """
630        try:
631            return _option.Some(self[index])
632        except IndexError:
633            return _option.NOTHING  # pyright: ignore
634
635    def insert(self, index: int, value: T) -> None:
636        """Insert an element at the position `index`.
637
638        Example
639        --------
640        ```py
641        vec = Vec((2, 3))
642        vec.insert(0, 1)
643        assert vec == [1, 2, 3]
644        ```
645        """
646        self.__setitem__(index, value)
647
648    def pop(self, index: int = -1) -> _option.Option[T]:
649        """Removes the last element from a vector and returns it, or `sain.Some(None)` if it is empty.
650
651        Example
652        -------
653        ```py
654        vec = Vec((1, 2, 3))
655        assert vec.pop() == Some(3)
656        assert vec == [1, 2]
657        ```
658        """
659        if not self._ptr:
660            return _option.NOTHING  # pyright: ignore
661
662        return _option.Some(self._ptr.pop(index))
663
664    def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]:
665        """Removes the last element from a vector and returns it if `f` returns `True`,
666        or `None` if it is empty.
667
668        Example
669        -------
670        ```py
671        vec = Vec((1, 2, 3))
672        assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
673        assert vec == [1, 2]
674        ```
675        """
676        if not self._ptr:
677            return _option.NOTHING  # pyright: ignore
678
679        if pred(self[-1]):
680            return self.pop()
681
682        return _option.NOTHING  # pyright: ignore
683
684    def dedup(self) -> None:
685        """Removes duplicate elements from `self` in-place.
686
687        Example
688        -------
689        ```py
690        vec = Vec([1, 2, 3, 3, 4, 1])
691        vec.dedup()
692        assert vec == [1, 2, 3, 4]
693        """
694
695        if not self._ptr:
696            return
697
698        seen: set[T] = set()
699        write_idx = 0
700
701        for read_idx, _ in enumerate(self._ptr):
702            if self._ptr[read_idx] not in seen:
703                seen.add(self._ptr[read_idx])
704                self._ptr[write_idx] = self._ptr[read_idx]
705                write_idx += 1
706
707        del self._ptr[write_idx:]
708
709    def remove(self, item: T) -> None:
710        """Remove the first appearance of `item` from this vector.
711
712        Example
713        -------
714        ```py
715        vec = Vec(('a', 'b', 'c'))
716        vec.remove('a')
717        assert vec == ['b', 'c']
718        ```
719        """
720        if not self._ptr:
721            return
722
723        self._ptr.remove(item)
724
725    def extend(self, iterable: collections.Iterable[T]) -> None:
726        """Extend this vector from another iterable.
727
728        Example
729        -------
730        ```py
731        vec = Vec((1, 2, 3))
732        vec.extend((4, 5, 6))
733
734        assert vec == [1, 2, 3, 4, 5, 6]
735        ```
736        """
737        if self._ptr is None:
738            self._ptr = []
739
740        self._ptr.extend(iterable)
741
742    def copy(self) -> Vec[T]:
743        """Create a vector that copies all of its elements and place it into the new one.
744
745        Example
746        -------
747        ```py
748        original = Vec((1,2,3))
749        copy = original.copy()
750        copy.push(4)
751
752        print(original) # [1, 2, 3]
753        ```
754        """
755        return Vec(self._ptr[:]) if self._ptr else Vec()
756
757    def clear(self) -> None:
758        """Clear all elements of this vector.
759
760        Example
761        -------
762        ```py
763        vec = Vec((1,2,3))
764        vec.clear()
765        assert vec.len() == 0
766        ```
767        """
768        if not self._ptr:
769            return
770
771        self._ptr.clear()
772
773    def sort(
774        self,
775        *,
776        key: collections.Callable[[T], SupportsRichComparison] | None = None,
777        reverse: bool = False,
778    ) -> None:
779        """This method sorts the list in place, using only < comparisons between items.
780
781        Example
782        -------
783        ```py
784        vec = Vec((2, 1, 3))
785        vec.sort()
786        assert vec == [1, 2, 3]
787        ```
788        """
789        if not self._ptr:
790            return
791
792        # key can be `None` here just fine, idk why pyright is complaining.
793        self._ptr.sort(key=key, reverse=reverse)  # pyright: ignore
794
795    def index(
796        self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize
797    ) -> int:
798        # << Official documentation >>
799        """Return zero-based index in the vec of the first item whose value is equal to `item`.
800        Raises a ValueError if there is no such item.
801
802        Example
803        -------
804        ```py
805        vec = Vec((1, 2, 3))
806        assert vec.index(2) == 1
807        ```
808        """
809        if self._ptr is None:
810            raise ValueError from None
811
812        return self._ptr.index(item, start, end)
813
814    def count(self, item: T) -> int:
815        """Return the number of occurrences of `item` in the vec.
816
817        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
818
819        Example
820        --------
821        ```py
822        vec = Vec((1, 2, 3, 3))
823        assert vec.count(3) == 2
824        ```
825        """
826        if self._ptr is None:
827            return 0
828
829        return self._ptr.count(item)
830
831    def __len__(self) -> int:
832        return len(self._ptr) if self._ptr else 0
833
834    def __setitem__(self, index: int, value: T):
835        if not self._ptr:
836            raise IndexError from None
837
838        self._ptr[index] = value
839
840    @typing.overload
841    def __getitem__(self, index: slice) -> Vec[T]: ...
842
843    @typing.overload
844    def __getitem__(self, index: int) -> T: ...
845
846    def __getitem__(self, index: int | slice) -> T | Vec[T]:
847        if not self._ptr:
848            raise IndexError("Index out of range")
849
850        if isinstance(index, slice):
851            return Vec(self._ptr[index])
852
853        return self._ptr[index]
854
855    def __delitem__(self, index: int) -> None:
856        if not self._ptr:
857            return
858
859        del self._ptr[index]
860
861    def __contains__(self, element: T) -> bool:
862        return element in self._ptr if self._ptr else False
863
864    def __iter__(self) -> collections.Iterator[T]:
865        if self._ptr is None:
866            return iter(())
867
868        return self._ptr.__iter__()
869
870    def __repr__(self) -> str:
871        return _LIST_REPR if not self._ptr else repr(self._ptr)
872
873    def __eq__(self, other: object) -> bool:
874        if isinstance(other, Vec):
875            return self._ptr == other._ptr
876
877        elif isinstance(other, list):
878            return self._ptr == other
879
880        return NotImplemented
881
882    def __ne__(self, other: object) -> bool:
883        return not self.__eq__(other)
884
885    def __le__(self, other: list[T]) -> bool:
886        if not self._ptr:
887            return False
888
889        return self._ptr <= other
890
891    def __ge__(self, other: list[T]) -> bool:
892        if not self._ptr:
893            return False
894
895        return self._ptr >= other
896
897    def __lt__(self, other: list[T]) -> bool:
898        if not self._ptr:
899            return False
900
901        return self._ptr < other
902
903    def __gt__(self, other: list[T]) -> bool:
904        if not self._ptr:
905            return False
906
907        return self._ptr > other
908
909    def __bool__(self) -> bool:
910        return bool(self._ptr)

A contiguous growable alternative to builtin list with extra functionalities.

The layout of Vec is almost the same as list.

When initializing a vec, it will not build the underlying list until the first element gets pushed. Which saves a little bit of memory.

Example
names = Vec()
names.push('foo')
names.push('bar')

print(names) # ['foo', 'bar']
assert names.len() == 2
Constructing
  • Vec(): Create an unallocated vec, Which means the underlying list will be None until you start pushing into it
  • Vec(other_list): Create a vec which points to other_list
  • Vec((1, 2, 3)): Create a vec with [1, 2, 3] pre-allocated
  • Vec.with_capacity(5): Create a vec that can hold up to 5 elements
  • from_args(1, 2, 3): Create a vec from arguments. This is not a classmethod

Iterating over Vec

There're two ways to iterate over a Vec. The first is to normally use for loop.

for i in names:
    print(i)

# foo
# bar

The second is to use Vec.iter, which yields all items in this Vec from start to end. Then the iterator gets exhausted as usual, See sain.Iterator.

iterator = names.iter()
for name in iterator.map(str.upper):
    print(name)

# FOO
# BAR

# No more items, The actual vec is left unchanged.
assert iterator.next().is_none()

Comparison operators

A Vec may be compared with another Vec or a list, which is what this type is built on-top of. any other type will return False

vec = Vec([1, 2, 3])
assert vec == [1, 2, 3] # True
assert not vec == (1, 2, 3)

Zero-Copy

A vec that gets initialized from a list will point to it and doesn't copy it. So any element that gets appended to the list will also get pushed into the vec thats pointing to it.

cells: list[str] = []
vec = Vec(cells) # This DOES NOT copy the `cells`.

cells.append("foo")
vec[0] == "foo"  # True

The opposite of the above is to initialize the vec from either an iterable or args, or copy the list.

from sain.collections import vec, Vec

# Creates a new vec and extend it with the elements.
from_args = vec.from_args("foo", "bar")

# inlined from another iterable.
from_iter = Vec(["foo", "bar"])

# Copy the list into a vec.
vec = Vec(cells[:])
cells.append("bar")

vec[1] # IndexError: "bar" doesn't exist in vec.
Vec(iterable: Iterable[~T] | None = None)
177    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
178        # We won't allocate to build the list here.
179        # Instead, On first push or fist indexed set
180        # we allocate if it was None.
181        if isinstance(iterable, list):
182            # Calling `list()` on another list will copy it, So instead we just point to it.
183            self._ptr = iterable
184        elif isinstance(iterable, Vec):
185            self._ptr = iterable._ptr
186        # any other iterable that ain't a list needs to get copied into a new list.
187        else:
188            self._ptr: list[T] | None = list(iterable) if iterable else None
189
190        self._capacity: int | None = None
@classmethod
def with_capacity(cls, capacity: int) -> Vec[~T]:
192    @classmethod
193    def with_capacity(cls, capacity: int) -> Vec[T]:
194        """Create a new `Vec` with at least the specified capacity.
195        This vec will be able to hold `capacity` elements without pushing further.
196
197        Check out `Vec.push_within_capacity` as well.
198
199        Example
200        -------
201        ```py
202        vec = Vec.with_capacity(3)
203        assert vec.len() == 0 and vec.capacity() >= 3
204
205        vec.push(1)
206        vec.push(2)
207        vec.push(3)
208        print(vec.len()) # 3
209
210        # This won't push.
211        vec.push(4)
212        ```
213        """
214        v = cls()
215        v._capacity = capacity
216        return v

Create a new Vec with at least the specified capacity. This vec will be able to hold capacity elements without pushing further.

Check out Vec.push_within_capacity as well.

Example
vec = Vec.with_capacity(3)
assert vec.len() == 0 and vec.capacity() >= 3

vec.push(1)
vec.push(2)
vec.push(3)
print(vec.len()) # 3

# This won't push.
vec.push(4)
def as_ref(self) -> Collection[~T]:
218    def as_ref(self) -> collections.Collection[T]:
219        """Return an immutable view over this vector elements.
220
221        This will *copy* `self` elements into a new tuple.
222
223        Example
224        -------
225        ```py
226        vec = Vec((1,2,3))
227        assert vec.as_ref() == (1, 2, 3)
228        ```
229        """
230        return tuple(self)

Return an immutable view over this vector elements.

This will copy self elements into a new tuple.

Example
vec = Vec((1,2,3))
assert vec.as_ref() == (1, 2, 3)
def len(self) -> int:
232    def len(self) -> int:
233        """Return the number of elements in this vector.
234
235        Example
236        -------
237        ```py
238        vec = Vec((1,2,3))
239
240        assert vec.len() == 3
241        ```
242        """
243        return self.__len__()

Return the number of elements in this vector.

Example
vec = Vec((1,2,3))

assert vec.len() == 3
def capacity(self) -> int:
245    def capacity(self) -> int:
246        """Return the capacity of this vector if set. 0 if not .
247
248        Example
249        -------
250        ```py
251        vec_with_cap = Vec.with_capacity(3)
252        assert vec_with_cap.capacity().unwrap() == 3
253
254        vec = Vec([1, 2, 3])
255        assert vec.capacity() == 0
256        ```
257        """
258        return 0 if self._capacity is None else self._capacity

Return the capacity of this vector if set. 0 if not .

Example
vec_with_cap = Vec.with_capacity(3)
assert vec_with_cap.capacity().unwrap() == 3

vec = Vec([1, 2, 3])
assert vec.capacity() == 0
def iter(self) -> Iterator[~T]:
260    def iter(self) -> _iter.Iterator[T]:
261        """Return an iterator over this vector elements.
262
263        Example
264        -------
265        ```py
266        vec = Vec([1 ,2, 3])
267        for element in vec.iter().map(str):
268            print(element)
269        ```
270        """
271        return _iter.Iter(self)

Return an iterator over this vector elements.

Example
vec = Vec([1 ,2, 3])
for element in vec.iter().map(str):
    print(element)
def is_empty(self) -> bool:
273    def is_empty(self) -> bool:
274        """Returns true if the vector contains no elements."""
275        return not self._ptr

Returns true if the vector contains no elements.

def split_off(self, at: int) -> Vec[~T]:
277    def split_off(self, at: int) -> Vec[T]:
278        """Split the vector off at the specified position, returning a new
279        vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`.
280
281        if this vec is empty, `self` is returned unchanged.
282
283        Example
284        -------
285        ```py
286        origin = Vec((1, 2, 3, 4))
287        split = vec.split_off(2)
288
289        print(origin, split)  # [1, 2], [3, 4]
290        ```
291
292        Raises
293        ------
294        `RuntimeError`
295            This method will raise if `at` > `len(self)`
296        """
297        len_ = self.len()
298        if at > len_:
299            raise RuntimeError(
300                f"Index `at` ({at}) should be <= than len of vector ({len_}) "
301            ) from None
302
303        # Either the list is empty or uninit.
304        if not self._ptr:
305            return self
306
307        split = self[at:len_]  # split the items into a new vec.
308        del self._ptr[at:len_]  # remove the items from the original list.
309        return split

Split the vector off at the specified position, returning a new vec at the range of [at : len], leaving self at [at : vec_len].

if this vec is empty, self is returned unchanged.

Example
origin = Vec((1, 2, 3, 4))
split = vec.split_off(2)

print(origin, split)  # [1, 2], [3, 4]
Raises
  • RuntimeError: This method will raise if at > len(self)
def split_first(self) -> '_option.Option[tuple[T, collections.Sequence[T]]]':
311    def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
312        """Split the first and rest elements of the vector, If empty, `None` is returned.
313
314        Example
315        -------
316        ```py
317        vec = Vec([1, 2, 3])
318        split = vec.split_first()
319        assert split == Some((1, [2, 3]))
320
321        vec: Vec[int] = Vec()
322        split = vec.split_first()
323        assert split.is_none()
324        ```
325        """
326        if not self._ptr:
327            return _option.NOTHING  # pyright: ignore
328
329        # optimized to only one element in the vector.
330        if self.len() == 1:
331            return _option.Some((self[0], ()))
332
333        first, *rest = self._ptr
334        return _option.Some((first, rest))

Split the first and rest elements of the vector, If empty, None is returned.

Example
vec = Vec([1, 2, 3])
split = vec.split_first()
assert split == Some((1, [2, 3]))

vec: Vec[int] = Vec()
split = vec.split_first()
assert split.is_none()
def split_last(self) -> '_option.Option[tuple[T, collections.Sequence[T]]]':
336    def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
337        """Split the last and rest elements of the vector, If empty, `None` is returned.
338
339        Example
340        -------
341        ```py
342        vec = Vec([1, 2, 3])
343        last, rest = vec.split_last().unwrap()
344        assert (last, rest) == [3, [1, 2]]
345        ```
346        """
347        if not self._ptr:
348            return _option.NOTHING  # pyright: ignore
349
350        # optimized to only one element in the vector.
351        if self.len() == 1:
352            return _option.Some((self[0], ()))
353
354        last = self[-1]
355        return _option.Some((last, [*self[:-1]]))

Split the last and rest elements of the vector, If empty, None is returned.

Example
vec = Vec([1, 2, 3])
last, rest = vec.split_last().unwrap()
assert (last, rest) == [3, [1, 2]]
def split_at( self, mid: int) -> tuple[Vec[~T], Vec[~T]]:
357    def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]:
358        """Divide `self` into two at an index.
359
360        The first will contain all elements from `[0:mid]` excluding `mid` it self.
361        and the second will contain the remaninig elements.
362
363        if `mid` > `self.len()`, Then all elements will be moved to the left,
364        returning an empty vec in right.
365
366        Example
367        -------
368        ```py
369        buffer = Vec((1, 2, 3, 4))
370        left, right = buffer.split_at(0)
371        assert left == [] and right == [1, 2, 3, 4]
372
373        left, right = buffer.split_at(2)
374        assert left == [1, 2] and right == [2, 3]
375        ```
376
377        The is roughly the implementation
378        ```py
379        self[0:mid], self[mid:]
380        ```
381        """
382        return self[0:mid], self[mid:]

Divide self into two at an index.

The first will contain all elements from [0:mid] excluding mid it self. and the second will contain the remaninig elements.

if mid > self.len(), Then all elements will be moved to the left, returning an empty vec in right.

Example
buffer = Vec((1, 2, 3, 4))
left, right = buffer.split_at(0)
assert left == [] and right == [1, 2, 3, 4]

left, right = buffer.split_at(2)
assert left == [1, 2] and right == [2, 3]

The is roughly the implementation

self[0:mid], self[mid:]
def swap(self, a: int, b: int):
384    def swap(self, a: int, b: int):
385        """Swap two elements in the vec.
386
387        if `a` equals to `b` then it's guaranteed that elements won't change value.
388
389        Example
390        -------
391        ```py
392        buf = Vec([1, 2, 3, 4])
393        buf.swap(0, 3)
394        assert buf == [4, 2, 3, 1]
395        ```
396
397        Raises
398        ------
399        IndexError
400            If the positions of `a` or `b` are out of index.
401        """
402        if self[a] == self[b]:
403            return
404
405        self[a], self[b] = self[b], self[a]

Swap two elements in the vec.

if a equals to b then it's guaranteed that elements won't change value.

Example
buf = Vec([1, 2, 3, 4])
buf.swap(0, 3)
assert buf == [4, 2, 3, 1]
Raises
  • IndexError: If the positions of a or b are out of index.
def swap_unchecked(self, a: int, b: int):
407    def swap_unchecked(self, a: int, b: int):
408        """Swap two elements in the vec. without checking if `a` == `b`.
409
410        If you care about `a` and `b` equality, see `Vec.swap`.
411
412        Example
413        -------
414        ```py
415        buf = Vec([1, 2, 3, 1])
416        buf.swap_unchecked(0, 3)
417        assert buf == [1, 2, 3, 1]
418        ```
419
420        Raises
421        ------
422        IndexError
423            If the positions of `a` or `b` are out of index.
424        """
425        self[a], self[b] = self[b], self[a]

Swap two elements in the vec. without checking if a == b.

If you care about a and b equality, see Vec.swap.

Example
buf = Vec([1, 2, 3, 1])
buf.swap_unchecked(0, 3)
assert buf == [1, 2, 3, 1]
Raises
  • IndexError: If the positions of a or b are out of index.
def first(self) -> '_option.Option[T]':
427    def first(self) -> _option.Option[T]:
428        """Get the first element in this vec, returning `None` if there's none.
429
430        Example
431        -------
432        ```py
433        vec = Vec((1,2,3))
434        first = vec.first()
435        assert ~first == 1
436        ```
437        """
438        return self.get(0)

Get the first element in this vec, returning None if there's none.

Example
vec = Vec((1,2,3))
first = vec.first()
assert ~first == 1
def last(self) -> '_option.Option[T]':
440    def last(self) -> _option.Option[T]:
441        """Get the last element in this vec, returning `None` if there's none.
442
443        Example
444        -------
445        ```py
446        vec = Vec([1, 2, 3, 4])
447        first = vec.last()
448        assert ~first == 4
449        ```
450        """
451        return self.get(-1)

Get the last element in this vec, returning None if there's none.

Example
vec = Vec([1, 2, 3, 4])
first = vec.last()
assert ~first == 4
def truncate(self, size: int) -> None:
453    def truncate(self, size: int) -> None:
454        """Shortens the vec, keeping the first `size` elements and dropping the rest.
455
456        Example
457        -------
458        ```py
459        vec = Vec([1,2,3])
460        vec.truncate(1)
461        assert vec == [1]
462        ```
463        """
464        if not self._ptr:
465            return
466
467        del self._ptr[size:]

Shortens the vec, keeping the first size elements and dropping the rest.

Example
vec = Vec([1,2,3])
vec.truncate(1)
assert vec == [1]
def retain(self, f: Callable[[~T], bool]) -> None:
469    def retain(self, f: collections.Callable[[T], bool]) -> None:
470        """Remove elements from this vec in-place while `f()` returns `True`.
471
472        In other words, filter this vector based on `f()`.
473
474        Example
475        -------
476        ```py
477        vec = Vec([1, 2, 3])
478        vec.retain(lambda elem: elem > 1)
479
480        assert vec == [2, 3]
481        ```
482        """
483        if not self._ptr:
484            return
485
486        for idx, e in enumerate(self._ptr):
487            if f(e):
488                del self._ptr[idx]

Remove elements from this vec in-place while f() returns True.

In other words, filter this vector based on f().

Example
vec = Vec([1, 2, 3])
vec.retain(lambda elem: elem > 1)

assert vec == [2, 3]
def swap_remove(self, item: ~T) -> ~T:
490    def swap_remove(self, item: T) -> T:
491        """Remove the first appearance of `item` from this vector and return it.
492
493        Raises
494        ------
495        * `ValueError`: if `item` is not in this vector.
496        * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it.
497
498        Example
499        -------
500        ```py
501        vec = Vec(('a', 'b', 'c'))
502        element = vec.remove('a')
503        assert vec == ['b', 'c'] and element == 'a'
504        ```
505        """
506        if self._ptr is None:
507            raise MemoryError("Vec is unallocated.") from None
508
509        return self._ptr.pop(self.index(item))

Remove the first appearance of item from this vector and return it.

Raises
  • * ValueError (if item is not in this vector.):

  • * MemoryError (if this vector hasn't allocated, Aka nothing has been pushed to it.):

Example
vec = Vec(('a', 'b', 'c'))
element = vec.remove('a')
assert vec == ['b', 'c'] and element == 'a'
def fill(self, value: ~T) -> None:
511    def fill(self, value: T) -> None:
512        """Fill `self` with the given `value`.
513
514        Nothing happens if the vec is empty or unallocated.
515
516        Example
517        ```py
518        a = Vec([0, 1, 2, 3])
519        a.fill(0)
520        assert a == [0, 0, 0, 0]
521        ```
522        """
523        if not self._ptr:
524            return
525
526        for n, _ in enumerate(self):
527            self[n] = value

Fill self with the given value.

Nothing happens if the vec is empty or unallocated.

Example

a = Vec([0, 1, 2, 3])
a.fill(0)
assert a == [0, 0, 0, 0]
def push(self, item: ~T) -> None:
529    def push(self, item: T) -> None:
530        """Push an element at the end of the vector.
531
532        Example
533        -------
534        ```py
535        vec = Vec()
536        vec.push(1)
537
538        assert vec == [1]
539        ```
540        """
541        if self._capacity is not None:
542            self.push_within_capacity(item)
543            return
544
545        if self._ptr is None:
546            self._ptr = []
547
548        self._ptr.append(item)

Push an element at the end of the vector.

Example
vec = Vec()
vec.push(1)

assert vec == [1]
def push_within_capacity(self, x: ~T) -> 'Result[None, T]':
550    def push_within_capacity(self, x: T) -> Result[None, T]:
551        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
552
553        Example
554        -------
555        ```py
556        vec: Vec[int] = Vec.with_capacity(3)
557        for i in range(3):
558            match vec.push_within_capacity(i):
559                case Ok(_):
560                    print("All good.")
561                case Err(elem):
562                    print("Reached max cap :< cant push", elem)
563        ```
564
565        Or you can also just call `Vec.push` and it will push if theres is sufficient capacity.
566        ```py
567        vec: Vec[int] = Vec.with_capacity(3)
568
569        vec.extend((1, 2, 3))
570        vec.push(4)
571
572        assert vec.len() == 3
573        ```
574        """
575        if self._ptr is None:
576            self._ptr = []
577
578        if self.len() == self._capacity:
579            return _result.Err(x)
580
581        self._ptr.append(x)
582        return _result.Ok(None)

Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.

Example
vec: Vec[int] = Vec.with_capacity(3)
for i in range(3):
    match vec.push_within_capacity(i):
        case Ok(_):
            print("All good.")
        case Err(elem):
            print("Reached max cap :< cant push", elem)

Or you can also just call Vec.push and it will push if theres is sufficient capacity.

vec: Vec[int] = Vec.with_capacity(3)

vec.extend((1, 2, 3))
vec.push(4)

assert vec.len() == 3
def reserve(self, additional: int) -> None:
584    def reserve(self, additional: int) -> None:
585        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
586
587        Example
588        -------
589        ```py
590        vec = Vec.with_capacity(3)
591        is_vip = random.choice((True, False))
592
593        for i in range(4):
594            match vec.push_within_capacity(i):
595                case Ok(_):
596                    print("All good")
597                case Err(person):
598                    # If the person is a VIP, then reserve for one more.
599                    if is_vip:
600                        vec.reserve(1)
601                        continue
602
603                    # is_vip was false.
604                    print("Can't reserve for non-VIP members...", person)
605                    break
606        ```
607        """
608        if self._capacity is not None:
609            self._capacity += additional

Reserves capacity for at least additional more elements to be inserted in the given Vec.

Example
vec = Vec.with_capacity(3)
is_vip = random.choice((True, False))

for i in range(4):
    match vec.push_within_capacity(i):
        case Ok(_):
            print("All good")
        case Err(person):
            # If the person is a VIP, then reserve for one more.
            if is_vip:
                vec.reserve(1)
                continue

            # is_vip was false.
            print("Can't reserve for non-VIP members...", person)
            break
def append(self, item: ~T) -> None:
529    def push(self, item: T) -> None:
530        """Push an element at the end of the vector.
531
532        Example
533        -------
534        ```py
535        vec = Vec()
536        vec.push(1)
537
538        assert vec == [1]
539        ```
540        """
541        if self._capacity is not None:
542            self.push_within_capacity(item)
543            return
544
545        if self._ptr is None:
546            self._ptr = []
547
548        self._ptr.append(item)

An alias for Vec.push method.

def get(self, index: int) -> '_option.Option[T]':
619    def get(self, index: int) -> _option.Option[T]:
620        """Get the item at the given index, or `Some[None]` if its out of bounds.
621
622        Example
623        -------
624        ```py
625        vec = Vec((1, 2, 3))
626        vec.get(0) == Some(1)
627        vec.get(3) == Some(None)
628        ```
629        """
630        try:
631            return _option.Some(self[index])
632        except IndexError:
633            return _option.NOTHING  # pyright: ignore

Get the item at the given index, or Some[None] if its out of bounds.

Example
vec = Vec((1, 2, 3))
vec.get(0) == Some(1)
vec.get(3) == Some(None)
def insert(self, index: int, value: ~T) -> None:
635    def insert(self, index: int, value: T) -> None:
636        """Insert an element at the position `index`.
637
638        Example
639        --------
640        ```py
641        vec = Vec((2, 3))
642        vec.insert(0, 1)
643        assert vec == [1, 2, 3]
644        ```
645        """
646        self.__setitem__(index, value)

Insert an element at the position index.

Example
vec = Vec((2, 3))
vec.insert(0, 1)
assert vec == [1, 2, 3]
def pop(self, index: int = -1) -> '_option.Option[T]':
648    def pop(self, index: int = -1) -> _option.Option[T]:
649        """Removes the last element from a vector and returns it, or `sain.Some(None)` if it is empty.
650
651        Example
652        -------
653        ```py
654        vec = Vec((1, 2, 3))
655        assert vec.pop() == Some(3)
656        assert vec == [1, 2]
657        ```
658        """
659        if not self._ptr:
660            return _option.NOTHING  # pyright: ignore
661
662        return _option.Some(self._ptr.pop(index))

Removes the last element from a vector and returns it, or sain.Some(None) if it is empty.

Example
vec = Vec((1, 2, 3))
assert vec.pop() == Some(3)
assert vec == [1, 2]
def pop_if(self, pred: Callable[[~T], bool]) -> '_option.Option[T]':
664    def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]:
665        """Removes the last element from a vector and returns it if `f` returns `True`,
666        or `None` if it is empty.
667
668        Example
669        -------
670        ```py
671        vec = Vec((1, 2, 3))
672        assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
673        assert vec == [1, 2]
674        ```
675        """
676        if not self._ptr:
677            return _option.NOTHING  # pyright: ignore
678
679        if pred(self[-1]):
680            return self.pop()
681
682        return _option.NOTHING  # pyright: ignore

Removes the last element from a vector and returns it if f returns True, or None if it is empty.

Example
vec = Vec((1, 2, 3))
assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
assert vec == [1, 2]
def dedup(self) -> None:
684    def dedup(self) -> None:
685        """Removes duplicate elements from `self` in-place.
686
687        Example
688        -------
689        ```py
690        vec = Vec([1, 2, 3, 3, 4, 1])
691        vec.dedup()
692        assert vec == [1, 2, 3, 4]
693        """
694
695        if not self._ptr:
696            return
697
698        seen: set[T] = set()
699        write_idx = 0
700
701        for read_idx, _ in enumerate(self._ptr):
702            if self._ptr[read_idx] not in seen:
703                seen.add(self._ptr[read_idx])
704                self._ptr[write_idx] = self._ptr[read_idx]
705                write_idx += 1
706
707        del self._ptr[write_idx:]

Removes duplicate elements from self in-place.

Example

```py vec = Vec([1, 2, 3, 3, 4, 1]) vec.dedup() assert vec == [1, 2, 3, 4]

def remove(self, item: ~T) -> None:
709    def remove(self, item: T) -> None:
710        """Remove the first appearance of `item` from this vector.
711
712        Example
713        -------
714        ```py
715        vec = Vec(('a', 'b', 'c'))
716        vec.remove('a')
717        assert vec == ['b', 'c']
718        ```
719        """
720        if not self._ptr:
721            return
722
723        self._ptr.remove(item)

Remove the first appearance of item from this vector.

Example
vec = Vec(('a', 'b', 'c'))
vec.remove('a')
assert vec == ['b', 'c']
def extend(self, iterable: Iterable[~T]) -> None:
725    def extend(self, iterable: collections.Iterable[T]) -> None:
726        """Extend this vector from another iterable.
727
728        Example
729        -------
730        ```py
731        vec = Vec((1, 2, 3))
732        vec.extend((4, 5, 6))
733
734        assert vec == [1, 2, 3, 4, 5, 6]
735        ```
736        """
737        if self._ptr is None:
738            self._ptr = []
739
740        self._ptr.extend(iterable)

Extend this vector from another iterable.

Example
vec = Vec((1, 2, 3))
vec.extend((4, 5, 6))

assert vec == [1, 2, 3, 4, 5, 6]
def copy(self) -> Vec[~T]:
742    def copy(self) -> Vec[T]:
743        """Create a vector that copies all of its elements and place it into the new one.
744
745        Example
746        -------
747        ```py
748        original = Vec((1,2,3))
749        copy = original.copy()
750        copy.push(4)
751
752        print(original) # [1, 2, 3]
753        ```
754        """
755        return Vec(self._ptr[:]) if self._ptr else Vec()

Create a vector that copies all of its elements and place it into the new one.

Example
original = Vec((1,2,3))
copy = original.copy()
copy.push(4)

print(original) # [1, 2, 3]
def clear(self) -> None:
757    def clear(self) -> None:
758        """Clear all elements of this vector.
759
760        Example
761        -------
762        ```py
763        vec = Vec((1,2,3))
764        vec.clear()
765        assert vec.len() == 0
766        ```
767        """
768        if not self._ptr:
769            return
770
771        self._ptr.clear()

Clear all elements of this vector.

Example
vec = Vec((1,2,3))
vec.clear()
assert vec.len() == 0
def sort( self, *, key: 'collections.Callable[[T], SupportsRichComparison] | None' = None, reverse: bool = False) -> None:
773    def sort(
774        self,
775        *,
776        key: collections.Callable[[T], SupportsRichComparison] | None = None,
777        reverse: bool = False,
778    ) -> None:
779        """This method sorts the list in place, using only < comparisons between items.
780
781        Example
782        -------
783        ```py
784        vec = Vec((2, 1, 3))
785        vec.sort()
786        assert vec == [1, 2, 3]
787        ```
788        """
789        if not self._ptr:
790            return
791
792        # key can be `None` here just fine, idk why pyright is complaining.
793        self._ptr.sort(key=key, reverse=reverse)  # pyright: ignore

This method sorts the list in place, using only < comparisons between items.

Example
vec = Vec((2, 1, 3))
vec.sort()
assert vec == [1, 2, 3]
def index( self, item: ~T, start: <class 'SupportsIndex'> = 0, end: int = 9223372036854775807) -> int:
795    def index(
796        self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize
797    ) -> int:
798        # << Official documentation >>
799        """Return zero-based index in the vec of the first item whose value is equal to `item`.
800        Raises a ValueError if there is no such item.
801
802        Example
803        -------
804        ```py
805        vec = Vec((1, 2, 3))
806        assert vec.index(2) == 1
807        ```
808        """
809        if self._ptr is None:
810            raise ValueError from None
811
812        return self._ptr.index(item, start, end)

Return zero-based index in the vec of the first item whose value is equal to item. Raises a ValueError if there is no such item.

Example
vec = Vec((1, 2, 3))
assert vec.index(2) == 1
def count(self, item: ~T) -> int:
814    def count(self, item: T) -> int:
815        """Return the number of occurrences of `item` in the vec.
816
817        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
818
819        Example
820        --------
821        ```py
822        vec = Vec((1, 2, 3, 3))
823        assert vec.count(3) == 2
824        ```
825        """
826        if self._ptr is None:
827            return 0
828
829        return self._ptr.count(item)

Return the number of occurrences of item in the vec.

0 is returned if the vector is empty or hasn't been initialized, as well if them item not found.

Example
vec = Vec((1, 2, 3, 3))
assert vec.count(3) == 2
@typing.runtime_checkable
class Error(sain.ToString, typing.Protocol):
 86@typing.runtime_checkable
 87class Error(ToString, typing.Protocol):
 88    """`Error` is an interface usually used for values that returns `sain.Result[T, E]`
 89
 90    where `E` is an implementation of this interface.
 91
 92    Example
 93    -------
 94    ```py
 95    import requests
 96    from dataclasses import dataclass
 97
 98    from sain import Error, Option, Some
 99    from sain import Result, Ok, Err
100
101    # Base error.
102    class HTTPError(Error): ...
103
104    @dataclass
105    class NotFound(HTTPError):
106        message = "The response returned [404]: not found."
107        http_status = 404
108        response: requests.Response
109
110        def description(self) -> str:
111            return "Couldn't find what you're looking for " + self.response.url
112
113        # It is not necessary to define this method,
114        # it just gives more context to the user handling this error.
115        def source(self) -> Option[type[HTTPError]]:
116            return Some(HTTPError)
117
118    @dataclass
119    class UserNotFound(NotFound):
120        user_id: int
121
122        def __post_init__(self) -> None:
123            request = self.response.request
124            self.message = f"User {self.user_id} fetched from {request.path_url} was not found."
125
126        # It is not necessary to define this method,
127        # it just gives more context to the user handling this error.
128        def source(self) -> Option[type[NotFound]]:
129            return Some(NotFound)
130
131        def description(self) -> str:
132            return f"Couldn't find the resource: {self.response.raw}."
133
134    # A simple request that handles [404] responses.
135    def request(
136        url: str,
137        resourceful: bool = False,
138        uid: int
139    ) -> Result[requests.Response, HTTPError]:
140        response = requests.get(
141            url,
142            json={"resourceful": True, "user_id": uid}
143            if resourceful else None
144        )
145
146        if response.status_code == 404:
147            if resourceful:
148                return Err(UserNotFound(response, uid))
149            return Err(NotFound(response))
150
151        return Ok(response)
152
153    # Execute the request
154    match request("some-url.com', True, uid=0):
155        case Ok(response):
156            # Deal with the response
157            ...
158        case Err(why):
159            # Deal with the error.
160            print(why.message)
161    ```
162    """
163
164    __slots__ = ("message",)
165
166    def __init__(self, message: str = "") -> None:
167        self.message = message
168        """A basic error message."""
169
170    def source(self) -> Option[type[Error]]:
171        """The source of this error, if any."""
172        return _option.NOTHING  # pyright: ignore
173
174    def description(self) -> str:
175        """Context for this error."""
176        return ""
177
178    def to_string(self) -> str:
179        return self.__repr__()
180
181    def __repr__(self) -> str:
182        source = None if (src := self.source()).is_none() else src
183        return (
184            f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})'
185        )
186
187    def __str__(self) -> str:
188        return self.message
189
190    # An error is always falsy.
191    def __bool__(self) -> typing.Literal[False]:
192        return False

Error is an interface usually used for values that returns sain.Result[T, E]

where E is an implementation of this interface.

Example
import requests
from dataclasses import dataclass

from sain import Error, Option, Some
from sain import Result, Ok, Err

# Base error.
class HTTPError(Error): ...

@dataclass
class NotFound(HTTPError):
    message = "The response returned [404]: not found."
    http_status = 404
    response: requests.Response

    def description(self) -> str:
        return "Couldn't find what you're looking for " + self.response.url

    # It is not necessary to define this method,
    # it just gives more context to the user handling this error.
    def source(self) -> Option[type[HTTPError]]:
        return Some(HTTPError)

@dataclass
class UserNotFound(NotFound):
    user_id: int

    def __post_init__(self) -> None:
        request = self.response.request
        self.message = f"User {self.user_id} fetched from {request.path_url} was not found."

    # It is not necessary to define this method,
    # it just gives more context to the user handling this error.
    def source(self) -> Option[type[NotFound]]:
        return Some(NotFound)

    def description(self) -> str:
        return f"Couldn't find the resource: {self.response.raw}."

# A simple request that handles [404] responses.
def request(
    url: str,
    resourceful: bool = False,
    uid: int
) -> Result[requests.Response, HTTPError]:
    response = requests.get(
        url,
        json={"resourceful": True, "user_id": uid}
        if resourceful else None
    )

    if response.status_code == 404:
        if resourceful:
            return Err(UserNotFound(response, uid))
        return Err(NotFound(response))

    return Ok(response)

# Execute the request
match request("some-url.com', True, uid=0):
    case Ok(response):
        # Deal with the response
        ...
    case Err(why):
        # Deal with the error.
        print(why.message)
Error(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
message

A basic error message.

def source(self) -> 'Option[type[Error]]':
170    def source(self) -> Option[type[Error]]:
171        """The source of this error, if any."""
172        return _option.NOTHING  # pyright: ignore

The source of this error, if any.

def description(self) -> str:
174    def description(self) -> str:
175        """Context for this error."""
176        return ""

Context for this error.

def to_string(self) -> str:
178    def to_string(self) -> str:
179        return self.__repr__()

Converts the given value to a str.

Example
i = 5  # assume `int` implements `ToString`
five = "5"
assert five == i.to_string()
@typing.final
class Box(typing.Generic[+T]):
 58@typing.final
 59class Box(typing.Generic[T]):
 60    """The box object for expiring data. not thread-safe.
 61
 62    A box is an object that contains a value of type `T` which expires it after the given amount of time,
 63    The box won't start expiring the data until its first access with `Box.get` method.
 64
 65    Example
 66    -------
 67    ```py
 68    # Initializing a box doesn't mean it started expiring. instead,
 69    # getting the value the first time will start the process.
 70    cache: dict[str, Box[int]] = {"sora": Box(999, timedelta(seconds=5)}
 71
 72    # first start expiring here.
 73    cache["sora"].get().unwrap()
 74    time.sleep(6)
 75    assert cache["sora"].has_expired()
 76    ```
 77    """
 78
 79    __slots__ = ("_inner", "_expire_in", "_on_expire", "_mono")
 80
 81    def __init__(self, value: T, expire_in: int | float | datetime.timedelta) -> None:
 82        if isinstance(expire_in, datetime.timedelta):
 83            expire_in = expire_in.total_seconds()
 84        else:
 85            expire_in = float(expire_in)
 86
 87        if expire_in <= 0:
 88            raise ValueError("expire_in must be more than 0 seconds.")
 89
 90        # We set the last call on the first access to the value.
 91        self._mono: float | None = None
 92        self._inner: Option[T] = option.Some(value)
 93        self._on_expire: collections.Callable[[T], typing.Any] | None = None
 94        self._expire_in = expire_in
 95
 96    @property
 97    def has_expired(self) -> bool:
 98        """Returns True if the value has expired."""
 99        # return self._mono is not None and not self._expire_in <= (
100        # time.monotonic() - self._mono
101        # )
102        return self._mono is not None and (
103            not self._mono or self._expire_in <= (time.monotonic() - self._mono)
104        )
105
106    def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self:
107        """Set a callback that will be invoked when this value gets expired.
108
109        Both async and sync callbacks are supported.
110
111        Example
112        -------
113        ```py
114        async def sink(message: str) -> None:
115            await client.create_message(message)
116            print("Sinked", message)
117
118        box = Box("bluh", 5).on_expire(sink)
119
120        while box.get().is_some():
121            time.sleep(5)
122        ```
123        First `.get` call on an expired box, the `sink` callback will be invoked,
124        also the inner value will be set to `Some(None)`.
125
126        After 5 seconds.
127        ```py
128        assert box.get() == Some("bluh") # This last call invokes the callback.
129        # Sinked bluh
130        assert box.get().is_none()
131        ```
132        """
133        self._on_expire = callback
134        return self
135
136    def remaining(self) -> float:
137        """Returns when this box will expire in seconds.
138
139        Example
140        --------
141        ```py
142        jogo = Box("jogo", 3)
143        assert jogo.get().unwrap() == "jogo"
144
145        time.sleep(1)
146        assert jogo.remaining() == 2
147        ```
148        """
149        if not self._mono:
150            return 0.0
151
152        return math.floor(
153            (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99
154        )
155
156    def get(self) -> Option[T]:
157        """Get the contained value if it was not expired, otherwise `Some(None)` is returned.
158
159        Example
160        -------
161        ```py
162        pizza = Box("pizza", timedelta(days=1))
163
164        while not pizza.get().is_none():
165            # Do stuff with the value while its not expired.
166
167        # After 1 day.
168        assert pizza.get().is_none()
169        ```
170        """
171        if self.has_expired:
172            if self._on_expire is not None:
173                try:
174                    if asyncio.iscoroutinefunction(self._on_expire):
175                        futures.loop().run_until_complete(
176                            self._on_expire(self._inner.unwrap_unchecked())
177                        )
178                    else:
179                        self._on_expire(self._inner.unwrap_unchecked())
180                finally:
181                    self._on_expire = None
182
183            self._inner.take()
184            self._mono = None
185            # SAFETY: The value is expired, therefore we always return None.
186            return option.NOTHING  # pyright: ignore
187
188        if self._mono is None:
189            self._mono = time.monotonic()
190
191        return self._inner
192
193    def __repr__(self) -> str:
194        return f"Box(value: {self._inner}, expired: {self.has_expired})"
195
196    __str__ = __repr__
197
198    def __eq__(self, value: object, /) -> bool:
199        if not isinstance(value, Box):
200            return NotImplemented
201
202        return (
203            self._inner == value._inner
204            and not self.has_expired
205            and not value.has_expired
206        )
207
208    def __ne__(self, value: object, /) -> bool:
209        return not self.__eq__(value)
210
211    def __hash__(self) -> int:
212        return hash(self._inner)
213
214    def __bool__(self) -> bool:
215        return not self.has_expired

The box object for expiring data. not thread-safe.

A box is an object that contains a value of type T which expires it after the given amount of time, The box won't start expiring the data until its first access with Box.get method.

Example
# Initializing a box doesn't mean it started expiring. instead,
# getting the value the first time will start the process.
cache: dict[str, Box[int]] = {"sora": Box(999, timedelta(seconds=5)}

# first start expiring here.
cache["sora"].get().unwrap()
time.sleep(6)
assert cache["sora"].has_expired()
Box(value: +T, expire_in: int | float | datetime.timedelta)
81    def __init__(self, value: T, expire_in: int | float | datetime.timedelta) -> None:
82        if isinstance(expire_in, datetime.timedelta):
83            expire_in = expire_in.total_seconds()
84        else:
85            expire_in = float(expire_in)
86
87        if expire_in <= 0:
88            raise ValueError("expire_in must be more than 0 seconds.")
89
90        # We set the last call on the first access to the value.
91        self._mono: float | None = None
92        self._inner: Option[T] = option.Some(value)
93        self._on_expire: collections.Callable[[T], typing.Any] | None = None
94        self._expire_in = expire_in
has_expired: bool
 96    @property
 97    def has_expired(self) -> bool:
 98        """Returns True if the value has expired."""
 99        # return self._mono is not None and not self._expire_in <= (
100        # time.monotonic() - self._mono
101        # )
102        return self._mono is not None and (
103            not self._mono or self._expire_in <= (time.monotonic() - self._mono)
104        )

Returns True if the value has expired.

def on_expire(self, callback: Callable[[+T], typing.Any]) -> typing_extensions.Self:
106    def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self:
107        """Set a callback that will be invoked when this value gets expired.
108
109        Both async and sync callbacks are supported.
110
111        Example
112        -------
113        ```py
114        async def sink(message: str) -> None:
115            await client.create_message(message)
116            print("Sinked", message)
117
118        box = Box("bluh", 5).on_expire(sink)
119
120        while box.get().is_some():
121            time.sleep(5)
122        ```
123        First `.get` call on an expired box, the `sink` callback will be invoked,
124        also the inner value will be set to `Some(None)`.
125
126        After 5 seconds.
127        ```py
128        assert box.get() == Some("bluh") # This last call invokes the callback.
129        # Sinked bluh
130        assert box.get().is_none()
131        ```
132        """
133        self._on_expire = callback
134        return self

Set a callback that will be invoked when this value gets expired.

Both async and sync callbacks are supported.

Example
async def sink(message: str) -> None:
    await client.create_message(message)
    print("Sinked", message)

box = Box("bluh", 5).on_expire(sink)

while box.get().is_some():
    time.sleep(5)

First .get call on an expired box, the sink callback will be invoked, also the inner value will be set to Some(None).

After 5 seconds.

assert box.get() == Some("bluh") # This last call invokes the callback.
# Sinked bluh
assert box.get().is_none()
def remaining(self) -> float:
136    def remaining(self) -> float:
137        """Returns when this box will expire in seconds.
138
139        Example
140        --------
141        ```py
142        jogo = Box("jogo", 3)
143        assert jogo.get().unwrap() == "jogo"
144
145        time.sleep(1)
146        assert jogo.remaining() == 2
147        ```
148        """
149        if not self._mono:
150            return 0.0
151
152        return math.floor(
153            (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99
154        )

Returns when this box will expire in seconds.

Example
jogo = Box("jogo", 3)
assert jogo.get().unwrap() == "jogo"

time.sleep(1)
assert jogo.remaining() == 2
def get(self) -> 'Option[T]':
156    def get(self) -> Option[T]:
157        """Get the contained value if it was not expired, otherwise `Some(None)` is returned.
158
159        Example
160        -------
161        ```py
162        pizza = Box("pizza", timedelta(days=1))
163
164        while not pizza.get().is_none():
165            # Do stuff with the value while its not expired.
166
167        # After 1 day.
168        assert pizza.get().is_none()
169        ```
170        """
171        if self.has_expired:
172            if self._on_expire is not None:
173                try:
174                    if asyncio.iscoroutinefunction(self._on_expire):
175                        futures.loop().run_until_complete(
176                            self._on_expire(self._inner.unwrap_unchecked())
177                        )
178                    else:
179                        self._on_expire(self._inner.unwrap_unchecked())
180                finally:
181                    self._on_expire = None
182
183            self._inner.take()
184            self._mono = None
185            # SAFETY: The value is expired, therefore we always return None.
186            return option.NOTHING  # pyright: ignore
187
188        if self._mono is None:
189            self._mono = time.monotonic()
190
191        return self._inner

Get the contained value if it was not expired, otherwise Some(None) is returned.

Example
pizza = Box("pizza", timedelta(days=1))

while not pizza.get().is_none():
    # Do stuff with the value while its not expired.

# After 1 day.
assert pizza.get().is_none()
@typing.runtime_checkable
class From(typing.Protocol[-T_co]):
115@typing.runtime_checkable
116class From(typing.Protocol[T_co]):
117    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
118
119    As the Rust documentation says, One should always prefer implementing From over Into
120    because implementing From automatically provides one with an implementation of Into.
121
122    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
123    that impl `From<T>`.
124
125    So for the sake of simplicity, You should implement whichever interface you want deal with,
126    Or simply, implement both as the same time.
127
128    Example
129    -------
130    ```py
131    @dataclass
132    class Id(From[str]):
133        value: int
134
135        @classmethod
136        def from_t(cls, value: str) -> Self:
137            return cls(value=int(value))
138
139    ```
140    """
141
142    __slots__ = ()
143
144    @classmethod
145    def from_t(cls, value: T_co) -> Self:
146        """Perform the conversion."""
147        raise NotImplementedError

Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.

As the Rust documentation says, One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into.

But there's no such thing in Python, as it's impossible to auto-impl Into<T> for all types that impl From<T>.

So for the sake of simplicity, You should implement whichever interface you want deal with, Or simply, implement both as the same time.

Example
@dataclass
class Id(From[str]):
    value: int

    @classmethod
    def from_t(cls, value: str) -> Self:
        return cls(value=int(value))
From(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
@classmethod
def from_t(cls, value: -T_co) -> typing_extensions.Self:
144    @classmethod
145    def from_t(cls, value: T_co) -> Self:
146        """Perform the conversion."""
147        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class TryFrom(typing.Protocol[-T_co, ~E]):
150@typing.runtime_checkable
151class TryFrom(typing.Protocol[T_co, E]):
152    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
153    It is the reciprocal of `TryInto`.
154
155    It is useful to implement this when you know that the conversion may fail in some way.
156
157    Generic Implementations
158    -------------------
159    This interface takes two type arguments, and return `Result<Self, E>`
160
161    * `T`: Which's the first generic `T` is the type that's being converted from.
162    * `E`: If the conversion fails in a way, this is what will return as the error.
163    * `Self`: Which's the instance of the class that is being converted into.
164
165    Example
166    -------
167    ```py
168    @dataclass
169    class Id(TryFrom[str, str]):
170        value: int
171
172        @classmethod
173        def try_from(cls, value: str) -> Result[Self, str]:
174            if not value.isnumeric():
175                # NaN
176                return Err(f"Couldn't convert: {value} to self")
177            # otherwise convert it to an Id instance.
178            return Ok(value=cls(int(value)))
179    ```
180    """
181
182    __slots__ = ()
183
184    @classmethod
185    def try_from(cls, value: T_co) -> Result[Self, E]:
186        """Perform the conversion."""
187        raise NotImplementedError

Simple and safe type conversions that may fail in a controlled way under some circumstances. It is the reciprocal of TryInto.

It is useful to implement this when you know that the conversion may fail in some way.

Generic Implementations

This interface takes two type arguments, and return Result<Self, E>

  • T: Which's the first generic T is the type that's being converted from.
  • E: If the conversion fails in a way, this is what will return as the error.
  • Self: Which's the instance of the class that is being converted into.
Example
@dataclass
class Id(TryFrom[str, str]):
    value: int

    @classmethod
    def try_from(cls, value: str) -> Result[Self, str]:
        if not value.isnumeric():
            # NaN
            return Err(f"Couldn't convert: {value} to self")
        # otherwise convert it to an Id instance.
        return Ok(value=cls(int(value)))
TryFrom(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
@classmethod
def try_from(cls, value: -T_co) -> 'Result[Self, E]':
184    @classmethod
185    def try_from(cls, value: T_co) -> Result[Self, E]:
186        """Perform the conversion."""
187        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class Into(typing.Protocol[+T_cov]):
190@typing.runtime_checkable
191class Into(typing.Protocol[T_cov]):
192    """Conversion from `self`, which may or may not be expensive.
193
194    Example
195    -------
196    ```py
197    @dataclass
198    class Id(Into[str]):
199        value: int
200
201        def into(self) -> str:
202            return str(self.value)
203    ```
204    """
205
206    __slots__ = ()
207
208    def into(self) -> T_cov:
209        """Perform the conversion."""
210        raise NotImplementedError

Conversion from self, which may or may not be expensive.

Example
@dataclass
class Id(Into[str]):
    value: int

    def into(self) -> str:
        return str(self.value)
Into(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
def into(self) -> +T_cov:
208    def into(self) -> T_cov:
209        """Perform the conversion."""
210        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class TryInto(typing.Protocol[~T, ~E]):
213@typing.runtime_checkable
214class TryInto(typing.Protocol[T, E]):
215    """An attempted conversion from `self`, which may or may not be expensive.
216
217    It is useful to implement this when you know that the conversion may fail in some way.
218
219    Generic Implementations
220    -------------------
221    This interface takes two type arguments, and return `Result<T, E>`
222
223    * `T`: The first generic `T` is the type that's being converted into.
224    * `E`: If the conversion fails in a way, this is what will return as the error.
225
226    Example
227    -------
228    ```py
229    @dataclass
230    class Id(TryInto[int, str]):
231        value: str
232
233        def try_into(self) -> Result[int, str]:
234            if not self.value.isnumeric():
235                return Err(f"{self.value} is not a number...")
236            return Ok(int(self.value))
237    ```
238    """
239
240    __slots__ = ()
241
242    def try_into(self) -> Result[T, E]:
243        """Perform the conversion."""
244        raise NotImplementedError

An attempted conversion from self, which may or may not be expensive.

It is useful to implement this when you know that the conversion may fail in some way.

Generic Implementations

This interface takes two type arguments, and return Result<T, E>

  • T: The first generic T is the type that's being converted into.
  • E: If the conversion fails in a way, this is what will return as the error.
Example
@dataclass
class Id(TryInto[int, str]):
    value: str

    def try_into(self) -> Result[int, str]:
        if not self.value.isnumeric():
            return Err(f"{self.value} is not a number...")
        return Ok(int(self.value))
TryInto(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
def try_into(self) -> 'Result[T, E]':
242    def try_into(self) -> Result[T, E]:
243        """Perform the conversion."""
244        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class ToString(typing.Protocol):
247@typing.runtime_checkable
248class ToString(typing.Protocol):
249    """A trait for explicitly converting a value to a `str`.
250
251    Example
252    -------
253    ```py
254    class Value[T: bytes](ToString):
255        buffer: T
256
257        def to_string(self) -> str:
258            return self.buffer.decode("utf-8")
259    ```
260    """
261
262    __slots__ = ()
263
264    def to_string(self) -> str:
265        """Converts the given value to a `str`.
266
267        Example
268        --------
269        ```py
270        i = 5  # assume `int` implements `ToString`
271        five = "5"
272        assert five == i.to_string()
273        ```
274        """
275        raise NotImplementedError
276
277    def __str__(self) -> str:
278        return self.to_string()

A trait for explicitly converting a value to a str.

Example
class Value[T: bytes](ToString):
    buffer: T

    def to_string(self) -> str:
        return self.buffer.decode("utf-8")
ToString(*args, **kwargs)
1430def _no_init_or_replace_init(self, *args, **kwargs):
1431    cls = type(self)
1432
1433    if cls._is_protocol:
1434        raise TypeError('Protocols cannot be instantiated')
1435
1436    # Already using a custom `__init__`. No need to calculate correct
1437    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1438    if cls.__init__ is not _no_init_or_replace_init:
1439        return
1440
1441    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1442    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1443    # searches for a proper new `__init__` in the MRO. The new `__init__`
1444    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1445    # instantiation of the protocol subclass will thus use the new
1446    # `__init__` and no longer call `_no_init_or_replace_init`.
1447    for base in cls.__mro__:
1448        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1449        if init is not _no_init_or_replace_init:
1450            cls.__init__ = init
1451            break
1452    else:
1453        # should not happen
1454        cls.__init__ = object.__init__
1455
1456    cls.__init__(self, *args, **kwargs)
def to_string(self) -> str:
264    def to_string(self) -> str:
265        """Converts the given value to a `str`.
266
267        Example
268        --------
269        ```py
270        i = 5  # assume `int` implements `ToString`
271        five = "5"
272        assert five == i.to_string()
273        ```
274        """
275        raise NotImplementedError

Converts the given value to a str.

Example
i = 5  # assume `int` implements `ToString`
five = "5"
assert five == i.to_string()