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    "Vec",
 68    # error.py
 69    "error",
 70    "Error",
 71    # boxed.py
 72    "boxed",
 73    "Box",
 74    # sync
 75    "sync",
 76    "MaybeUninit",
 77    # convert
 78    "convert",
 79    "From",
 80    "TryFrom",
 81    "Into",
 82    "TryInto",
 83)
 84
 85from . import boxed
 86from . import collections
 87from . import convert
 88from . import default
 89from . import error
 90from . import futures
 91from . import iter
 92from . import macros
 93from . import option
 94from . import result
 95from . import sync
 96from .boxed import Box
 97from .cfg import cfg
 98from .cfg import cfg_attr
 99from .collections import Vec
100from .collections import vec
101from .convert import From
102from .convert import Into
103from .convert import TryFrom
104from .convert import TryInto
105from .default import Default
106from .error import Error
107from .iter import Iter
108from .iter import Iterator
109from .macros import deprecated
110from .macros import doc
111from .macros import todo
112from .macros import unimplemented
113from .maybe_uninit import MaybeUninit
114from .option import NOTHING
115from .option import Option
116from .option import Some
117from .result import Err
118from .result import Ok
119from .result import Result
120
121__version__: str = "1.1.0"
122__url__: str = "https://github.com/nxtlo/sain"
123__author__: str = "nxtlo"
124__about__: str = (
125    "Sain is a dependency-free library that implements some of the Rust core"
126    "types which abstracts over common patterns in Rust for Python."
127)
128__license__: str = "BSD 3-Clause License"
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:
172def cfg(
173    target_os: System | None = None,
174    python_version: tuple[int, int, int] | None = None,
175    target_arch: Arch | None = None,
176    impl: Python | None = None,
177) -> bool:
178    """A function that will run the code only if all predicate attributes returns `True`.
179
180    The difference between this function and `cfg_attr` is that this function will not raise an exception.
181    Instead it will return `False` if any of the attributes fails.
182
183    Example
184    -------
185    ```py
186    import sain
187
188    if cfg(target_os="windows"):
189        print("Windows")
190    elif cfg(target_os="linux", target_arch="arm64"):
191        print("Linux")
192    else:
193        print("Something else")
194    ```
195
196    Parameters
197    ----------
198    target_os : `str | None`
199        The targeted operating system thats required for the object to be ran.
200    python_version : `tuple[int, int, int] | None`
201        The targeted Python version thats required for the object to be ran. Format must be `(3, 9, 5)``
202    target_arch : `str | None`
203        The CPU targeted architecture thats required for the object to be ran.
204    impl : `str | None`
205        The Python implementation thats required for the object to be ran.
206
207    Returns
208    -------
209    `bool`
210        The condition that was checked.
211    """
212    checker = _AttrCheck(
213        lambda: None,
214        no_raise=True,
215        target_os=target_os,
216        python_version=python_version,
217        target_arch=target_arch,
218        impl=impl,
219    )
220    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.
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) -> collections.abc.Callable[[~F], ~F]:
 99def cfg_attr(
100    *,
101    target_os: System | None = None,
102    python_version: tuple[int, int, int] | None = None,
103    target_arch: Arch | None = None,
104    impl: Python | None = None,
105) -> collections.Callable[[F], F]:
106    """Conditional runtime object configuration based on passed arguments.
107
108    If the decorated object gets called and one of the attributes returns `False`,
109    `RuntimeError` will be raised and the object will not run.
110
111    Example
112    -------
113    ```py
114    import sain
115
116    @cfg_attr(target_os="windows")
117    def windows_only():
118        # Do stuff with Windows's API.
119        ...
120
121    # Mut be PyPy Python implementation or `RuntimeError` will be raised
122    # when creating the instance.
123    @cfg_attr(impl="PyPy")
124    class Zoo:
125        @sain.cfg_attr(target_os="linux")
126        def bark(self) -> None:
127            windows_only()  # RuntimeError("Windows OS only!)
128
129    # An instance will not be created if raised.
130    zoo = Zoo()
131    # RuntimeError("class Zoo requires PyPy implementation")
132    zoo.bark()
133    # Whats zoo??
134    ```
135
136    Parameters
137    ----------
138    target_os : `str | None`
139        The targeted operating system thats required for the object.
140    python_version : `tuple[int, int, int] | None`
141        The targeted Python version thats required for the object. Format must be `(3, 9, 5)`.
142    target_arch : `str | None`
143        The CPU targeted architecture thats required for the object.
144    impl : `str | None`
145        The Python implementation thats required for the object.
146
147    Raises
148    ------
149    `RuntimeError`
150        This fails if any of the attributes returns `False`.
151    `ValueError`
152        If the passed Python implementation is unknown.
153    """
154
155    def decorator(callback: F) -> F:
156        @functools.wraps(callback)
157        def wrapper(*args: typing.Any, **kwargs: typing.Any) -> F:
158            checker = _AttrCheck(
159                callback,
160                target_os=target_os,
161                python_version=python_version,
162                target_arch=target_arch,
163                impl=impl,
164            )
165            return checker(*args, **kwargs)
166
167        return typing.cast(F, wrapper)
168
169    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:
        windows_only()  # RuntimeError("Windows OS only!)

# An instance will not be created if raised.
zoo = Zoo()
# RuntimeError("class Zoo requires PyPy implementation")
zoo.bark()
# Whats zoo??
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.
@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)
1739def _no_init_or_replace_init(self, *args, **kwargs):
1740    cls = type(self)
1741
1742    if cls._is_protocol:
1743        raise TypeError('Protocols cannot be instantiated')
1744
1745    # Already using a custom `__init__`. No need to calculate correct
1746    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1747    if cls.__init__ is not _no_init_or_replace_init:
1748        return
1749
1750    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1751    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1752    # searches for a proper new `__init__` in the MRO. The new `__init__`
1753    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1754    # instantiation of the protocol subclass will thus use the new
1755    # `__init__` and no longer call `_no_init_or_replace_init`.
1756    for base in cls.__mro__:
1757        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1758        if init is not _no_init_or_replace_init:
1759            cls.__init__ = init
1760            break
1761    else:
1762        # should not happen
1763        cls.__init__ = object.__init__
1764
1765    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", "__default")
 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 into_inner(self) -> T | None:
104        """Consume `Self`, returning the wrapped value as `T | None`.
105
106        Examples
107        --------
108        ```py
109        opt = Some('char')
110        x = opt.into_inner()
111        assert x is not None
112
113        opt = Some(None)
114        assert opt.into_inner() 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(
143                f"Called `Option.unwrap()` on {type(self._value).__name__}."
144            ) from None
145
146        return self._value
147
148    def unwrap_or(self, default: T, /) -> T:
149        """Unwrap the inner value either returning if its not `None` or returning `default`.
150
151        Example
152        -------
153        ```py
154        value = Some(5)
155        print(value.unwrap_or(10))
156        # 5
157
158        # Type hint is required here.
159        value: Option[int] = Some(None)
160        print(value.unwrap_or(10))
161        # 10
162        ```
163        """
164        if self._value is None:
165            return default
166
167        return self._value
168
169    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
170        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
171
172        Example
173        -------
174        ```py
175        value = Some(5)
176        print(value.unwrap_or_else(lambda: 10))
177        # 5
178
179        value: Option[bool] = Some(None)
180        print(value.unwrap_or_else(lambda: True))
181        # True
182        ```
183        """
184        if self._value is None:
185            return f()
186
187        return self._value
188
189    @macros.unsafe
190    def unwrap_unchecked(self) -> T:
191        """Returns the contained Some value without checking that the value is not None.
192
193        Example
194        -------
195        ```py
196        v: Option[float] = Some(1.2)
197        v.unwrap_unchecked() # 1.2
198
199        v: Option[float] = Some(None)
200        print(v.unwrap_unchecked()) # Undefined Behavior
201        ```
202        """
203        #! SAFETY: The caller guarantees that the value is not None.
204        return self._value  # type: ignore
205
206    def expect(self, message: str, /) -> T:
207        """Returns the contained value if it is not `None` otherwise raises a `RuntimeError`.
208
209        Example
210        -------
211        ```py
212        value = Some("Hello")
213
214        print(value.expect("Value is None"))
215        # "Hello"
216
217        value: Option[str] = Some(None)
218        print(value.expect("Value is None"))
219        # RuntimeError("Value is None")
220        ```
221        """
222        if self._value is None:
223            raise RuntimeError(message)
224
225        return self._value
226
227    # *- Functional operations -*
228    def map(self, f: Fn[T, U], /) -> Some[U]:
229        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
230
231        Example
232        -------
233        ```py
234        value = Some(5.0)
235
236        print(value.map(lambda x: x * 2.0))
237        # Some(10.0)
238
239        value: Option[bool] = Some(None)
240        print(value)
241        # Some(None)
242        ```
243        """
244        if self._value is None:
245            return nothing_unchecked()
246
247        return Some(f(self._value))
248
249    def map_or(self, default: U, f: Fn[T, U], /) -> U:
250        """Map the inner value to another type or return `default` if its `None`.
251
252        Example
253        -------
254        ```py
255        value: Option[float] = Some(5.0)
256
257        # map to int.
258        print(value.map_or(0, int))
259        # 6
260
261        value: Option[float] = Some(None)
262        print(value.map_or(0, int)
263        # 0
264        ```
265        """
266        if self._value is None:
267            return default
268
269        return f(self._value)
270
271    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
272        """Map the inner value to another type, or return `default()` if its `None`.
273
274        Example
275        -------
276        ```py
277        def default() -> int:
278            return sys.getsizeof(object())
279
280        value: Option[float] = Some(5.0)
281
282        # map to int.
283        print(value.map_or_else(default, int))
284        # 6
285
286        value: Option[float] = Some(None)
287        print(value.map_or_else(default, int)
288        # 28 <- size of object()
289        ```
290        """
291        if self._value is None:
292            return default()
293
294        return f(self._value)
295
296    def filter(self, predicate: Fn[T, bool]) -> Some[T]:
297        """Returns `Some(None)` if the contained value is `None`,
298
299        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
300
301        Example
302        -------
303        ```py
304        value = Some([1, 2, 3])
305
306        print(value.filter(lambda x: 1 in x))
307        # Some([1, 2, 3])
308
309        value: Option[int] = Some([1, 2, 3]) # or Some(None)
310        print(value.filter(lambda x: 1 not in x))
311        # None
312        ```
313        """
314        if (value := self._value) is not None:
315            if predicate(value):
316                return Some(value)
317
318        return nothing_unchecked()
319
320    # *- Inner operations *-
321
322    def take(self) -> Option[T]:
323        """Take the value from `Self`, Setting it to `None`.
324
325        Example
326        -------
327        ```py
328        original = Some("Hi")
329        new = original.take()
330
331        print(original, new)
332        # None, Some("Hi")
333        ```
334        """
335        if self._value is None:
336            return nothing_unchecked()
337
338        val = self._value
339        self._value = None
340        return Some(val)
341
342    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
343        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
344
345        Example
346        -------
347        ```py
348        def validate(email: str) -> bool:
349            # you can obviously validate this better.
350            return email.find('@') == 1
351
352        original = Some("flex@gg.com")
353        valid = original.take_if(validate)
354        assert is_allowed.is_some() and original.is_none()
355
356        original = Some("mail.example.com")
357        invalid = original.take_if(validate)
358        assert invalid.is_none() and original.is_some()
359        ```
360        """
361        if self.map_or(False, predicate):
362            return self.take()
363
364        return nothing_unchecked()
365
366    def replace(self, value: T) -> Some[T]:
367        """Replace the contained value with another value.
368
369        Example
370        -------
371        ```py
372        value: Option[str] = Some(None)
373        value = value.replace("Hello")
374        # Some("Hello")
375        ```
376        """
377        self._value = value
378        return self
379
380    def and_ok(self, optb: Some[T]) -> Some[T]:
381        """Returns `Some(None)` if either contained value is `None`,
382
383        Otherwise return `optb`.
384
385        Example
386        -------
387        ```py
388        x: Option[int] = Some(None)
389        y: Option[str] = Some("bye")
390        assert x.and_ok(y).is_none()
391
392        x: Option[int] = Some(10)
393        y: Option[str] = Some("bye")
394        assert value.and_ok(y) == Some("bye")
395        ```
396        """
397        if self._value is None or optb.is_none():
398            return nothing_unchecked()
399
400        return optb
401
402    def and_then(self, f: Fn[T, Some[T]]) -> Some[T]:
403        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
404        on `T` and return `Option[T]` if it's value not `None`.
405
406        Example
407        -------
408        ```py
409        value = Some(5)
410        print(value.and_then(lambda x: Some(x * 2)))
411        # Some(10)
412
413        value: Option[int] = Some(10)
414        print(value.and_then(lambda x: Some(None)))
415        # Some(None)
416        ```
417        """
418        if self._value is None:
419            return nothing_unchecked()
420
421        return f(self._value)
422
423    # *- Builder methods *-
424
425    def iter(self) -> _iter.Iterator[T]:
426        """Returns an iterator over the contained value.
427
428        Example
429        -------
430        ```py
431        from sain import Some
432        value = Some("gg")
433        value.next() == Some("gg")
434
435        value: Option[int] = Some(None)
436        value.next().is_none()
437        ```
438        """
439        if self._value is None:
440            #! SAFETY: We know the value is None here.
441            return _iter.empty()
442
443        return _iter.once(self._value)
444
445    def as_ref(self) -> Some[cell.Cell[T]]:
446        """Returns immutable `Some[sain.cell.Cell[T]]` if the contained value is not `None`,
447
448        Otherwise returns `Some(None)`.
449
450        Example
451        -------
452        ```py
453        value = Some(5).as_ref().unwrap()
454        value.object = 0 # FrozenError!
455
456        owned = value.object
457        print(owned) # 5
458
459        # Create a copy of object.
460        clone = value.copy()
461        clone = 0  # Thats fine.
462        print(clone == owned) # False, 0 != 5
463
464        # None object.
465        value: Option[int] = Some(None)
466        print(value.as_ref())
467        # Some(None)
468        ```
469
470        Raises
471        ------
472        `dataclasses.FrozenInstanceError`
473            When attempting to modify the contained value. Use `sain.AsRef.copy()` method to create a copy.
474
475            Or just use `.as_mut()` if you're dealing with mutable objects.
476        """
477        if self._value is not None:
478            return Some(cell.Cell(self._value))
479
480        # SAFETY: self._value is None.
481        return NOTHING  # pyright: ignore
482
483    def as_mut(self) -> Some[cell.RefCell[T]]:
484        """Returns mutable `Some[sain.cell.RefCell[T]]` if the contained value is not `None`,
485
486        Otherwise returns `Some(None)`.
487
488        Example
489        -------
490        ```py
491        value = Some(5).as_mut().unwrap()
492        value.object = 0
493        print(value) # Some(RefCell(0))
494
495        # None object.
496        value: Option[int] = Some(None)
497        print(value.as_mut())
498        # Some(None)
499        ```
500        """
501        if self._value is not None:
502            return Some(cell.RefCell(self._value))
503
504        # SAFETY: self._value is None.
505        return NOTHING  # pyright: ignore
506
507    # *- Boolean checks *-
508
509    def is_some(self) -> bool:
510        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
511
512        Example
513        -------
514        ```py
515        value = Some(5)
516        print(value.is_some())
517        # True
518
519        value: Option[int] = Some(None)
520        print(value.is_some())
521        # False
522        ```
523        """
524        return self._value is not None
525
526    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
527        """Returns `True` if the contained value is not `None` and
528        the predicate returns `True`, otherwise returns `False`.
529
530        Example
531        -------
532        ```py
533        value = Some(5)
534        print(value.is_some_and(lambda x: x > 3))
535        # True
536
537        value: Option[int] = Some(None)
538        print(value.is_some_and(lambda x: x > 3))
539        # False
540        ```
541        """
542        return self._value is not None and predicate(self._value)
543
544    def is_none(self) -> bool:
545        """Returns `True` if the contained value is `None`, otherwise returns `False`.
546
547        Example
548        -------
549        ```py
550        value = Some(5)
551        print(value.is_none())
552        # False
553
554        value: Option[int] = Some(None)
555        print(value.is_none())
556        # True
557        ```
558        """
559        return self._value is None
560
561    def __repr__(self) -> str:
562        if self._value is None:
563            return "None"
564        return f"Some({self._value!r})"
565
566    __str__ = __repr__
567
568    def __invert__(self) -> T:
569        return self.unwrap()
570
571    def __or__(self, other: T) -> T:
572        return self.unwrap_or(other)
573
574    def __bool__(self) -> bool:
575        return self.is_some()
576
577    def __eq__(self, other: object) -> bool:
578        if not isinstance(other, Some):
579            return NotImplemented
580
581        return self._value == other._value  # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType]
582
583    def __ne__(self, other: object) -> bool:
584        return not self.__eq__(other)
585
586    def __hash__(self) -> int:
587        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() -> Some[NoneType]:
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 into_inner(self) -> Optional[~T]:
103    def into_inner(self) -> T | None:
104        """Consume `Self`, returning the wrapped value as `T | None`.
105
106        Examples
107        --------
108        ```py
109        opt = Some('char')
110        x = opt.into_inner()
111        assert x is not None
112
113        opt = Some(None)
114        assert opt.into_inner() is None
115        ```
116        """
117        return self._value

Consume Self, returning the wrapped value as T | None.

Examples
opt = Some('char')
x = opt.into_inner()
assert x is not None

opt = Some(None)
assert opt.into_inner() 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(
143                f"Called `Option.unwrap()` on {type(self._value).__name__}."
144            ) from None
145
146        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:
148    def unwrap_or(self, default: T, /) -> T:
149        """Unwrap the inner value either returning if its not `None` or returning `default`.
150
151        Example
152        -------
153        ```py
154        value = Some(5)
155        print(value.unwrap_or(10))
156        # 5
157
158        # Type hint is required here.
159        value: Option[int] = Some(None)
160        print(value.unwrap_or(10))
161        # 10
162        ```
163        """
164        if self._value is None:
165            return default
166
167        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: collections.abc.Callable[[], ~T], /) -> ~T:
169    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
170        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
171
172        Example
173        -------
174        ```py
175        value = Some(5)
176        print(value.unwrap_or_else(lambda: 10))
177        # 5
178
179        value: Option[bool] = Some(None)
180        print(value.unwrap_or_else(lambda: True))
181        # True
182        ```
183        """
184        if self._value is None:
185            return f()
186
187        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:
189    @macros.unsafe
190    def unwrap_unchecked(self) -> T:
191        """Returns the contained Some value without checking that the value is not None.
192
193        Example
194        -------
195        ```py
196        v: Option[float] = Some(1.2)
197        v.unwrap_unchecked() # 1.2
198
199        v: Option[float] = Some(None)
200        print(v.unwrap_unchecked()) # Undefined Behavior
201        ```
202        """
203        #! SAFETY: The caller guarantees that the value is not None.
204        return self._value  # type: 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:
206    def expect(self, message: str, /) -> T:
207        """Returns the contained value if it is not `None` otherwise raises a `RuntimeError`.
208
209        Example
210        -------
211        ```py
212        value = Some("Hello")
213
214        print(value.expect("Value is None"))
215        # "Hello"
216
217        value: Option[str] = Some(None)
218        print(value.expect("Value is None"))
219        # RuntimeError("Value is None")
220        ```
221        """
222        if self._value is None:
223            raise RuntimeError(message)
224
225        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: collections.abc.Callable[[~T], ~U], /) -> Some[~U]:
228    def map(self, f: Fn[T, U], /) -> Some[U]:
229        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
230
231        Example
232        -------
233        ```py
234        value = Some(5.0)
235
236        print(value.map(lambda x: x * 2.0))
237        # Some(10.0)
238
239        value: Option[bool] = Some(None)
240        print(value)
241        # Some(None)
242        ```
243        """
244        if self._value is None:
245            return nothing_unchecked()
246
247        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: collections.abc.Callable[[~T], ~U], /) -> ~U:
249    def map_or(self, default: U, f: Fn[T, U], /) -> U:
250        """Map the inner value to another type or return `default` if its `None`.
251
252        Example
253        -------
254        ```py
255        value: Option[float] = Some(5.0)
256
257        # map to int.
258        print(value.map_or(0, int))
259        # 6
260
261        value: Option[float] = Some(None)
262        print(value.map_or(0, int)
263        # 0
264        ```
265        """
266        if self._value is None:
267            return default
268
269        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: collections.abc.Callable[[], ~U], f: collections.abc.Callable[[~T], ~U], /) -> ~U:
271    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
272        """Map the inner value to another type, or return `default()` if its `None`.
273
274        Example
275        -------
276        ```py
277        def default() -> int:
278            return sys.getsizeof(object())
279
280        value: Option[float] = Some(5.0)
281
282        # map to int.
283        print(value.map_or_else(default, int))
284        # 6
285
286        value: Option[float] = Some(None)
287        print(value.map_or_else(default, int)
288        # 28 <- size of object()
289        ```
290        """
291        if self._value is None:
292            return default()
293
294        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: collections.abc.Callable[[~T], bool]) -> Some[~T]:
296    def filter(self, predicate: Fn[T, bool]) -> Some[T]:
297        """Returns `Some(None)` if the contained value is `None`,
298
299        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
300
301        Example
302        -------
303        ```py
304        value = Some([1, 2, 3])
305
306        print(value.filter(lambda x: 1 in x))
307        # Some([1, 2, 3])
308
309        value: Option[int] = Some([1, 2, 3]) # or Some(None)
310        print(value.filter(lambda x: 1 not in x))
311        # None
312        ```
313        """
314        if (value := self._value) is not None:
315            if predicate(value):
316                return Some(value)
317
318        return nothing_unchecked()

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 take(self) -> Some[~T]:
322    def take(self) -> Option[T]:
323        """Take the value from `Self`, Setting it to `None`.
324
325        Example
326        -------
327        ```py
328        original = Some("Hi")
329        new = original.take()
330
331        print(original, new)
332        # None, Some("Hi")
333        ```
334        """
335        if self._value is None:
336            return nothing_unchecked()
337
338        val = self._value
339        self._value = None
340        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: collections.abc.Callable[[~T], bool]) -> Some[~T]:
342    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
343        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
344
345        Example
346        -------
347        ```py
348        def validate(email: str) -> bool:
349            # you can obviously validate this better.
350            return email.find('@') == 1
351
352        original = Some("flex@gg.com")
353        valid = original.take_if(validate)
354        assert is_allowed.is_some() and original.is_none()
355
356        original = Some("mail.example.com")
357        invalid = original.take_if(validate)
358        assert invalid.is_none() and original.is_some()
359        ```
360        """
361        if self.map_or(False, predicate):
362            return self.take()
363
364        return nothing_unchecked()

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) -> Some[~T]:
366    def replace(self, value: T) -> Some[T]:
367        """Replace the contained value with another value.
368
369        Example
370        -------
371        ```py
372        value: Option[str] = Some(None)
373        value = value.replace("Hello")
374        # Some("Hello")
375        ```
376        """
377        self._value = value
378        return self

Replace the contained value with another value.

Example
value: Option[str] = Some(None)
value = value.replace("Hello")
# Some("Hello")
def and_ok(self, optb: Some[~T]) -> Some[~T]:
380    def and_ok(self, optb: Some[T]) -> Some[T]:
381        """Returns `Some(None)` if either contained value is `None`,
382
383        Otherwise return `optb`.
384
385        Example
386        -------
387        ```py
388        x: Option[int] = Some(None)
389        y: Option[str] = Some("bye")
390        assert x.and_ok(y).is_none()
391
392        x: Option[int] = Some(10)
393        y: Option[str] = Some("bye")
394        assert value.and_ok(y) == Some("bye")
395        ```
396        """
397        if self._value is None or optb.is_none():
398            return nothing_unchecked()
399
400        return optb

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

Otherwise return optb.

Example
x: Option[int] = Some(None)
y: Option[str] = Some("bye")
assert x.and_ok(y).is_none()

x: Option[int] = Some(10)
y: Option[str] = Some("bye")
assert value.and_ok(y) == Some("bye")
def and_then( self, f: collections.abc.Callable[[~T], Some[~T]]) -> Some[~T]:
402    def and_then(self, f: Fn[T, Some[T]]) -> Some[T]:
403        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
404        on `T` and return `Option[T]` if it's value not `None`.
405
406        Example
407        -------
408        ```py
409        value = Some(5)
410        print(value.and_then(lambda x: Some(x * 2)))
411        # Some(10)
412
413        value: Option[int] = Some(10)
414        print(value.and_then(lambda x: Some(None)))
415        # Some(None)
416        ```
417        """
418        if self._value is None:
419            return nothing_unchecked()
420
421        return f(self._value)

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

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

value: Option[int] = Some(10)
print(value.and_then(lambda x: Some(None)))
# Some(None)
def iter(self) -> Iterator[~T]:
425    def iter(self) -> _iter.Iterator[T]:
426        """Returns an iterator over the contained value.
427
428        Example
429        -------
430        ```py
431        from sain import Some
432        value = Some("gg")
433        value.next() == Some("gg")
434
435        value: Option[int] = Some(None)
436        value.next().is_none()
437        ```
438        """
439        if self._value is None:
440            #! SAFETY: We know the value is None here.
441            return _iter.empty()
442
443        return _iter.once(self._value)

Returns an iterator over the contained value.

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

value: Option[int] = Some(None)
value.next().is_none()
def as_ref(self) -> Some[sain.cell.Cell[~T]]:
445    def as_ref(self) -> Some[cell.Cell[T]]:
446        """Returns immutable `Some[sain.cell.Cell[T]]` if the contained value is not `None`,
447
448        Otherwise returns `Some(None)`.
449
450        Example
451        -------
452        ```py
453        value = Some(5).as_ref().unwrap()
454        value.object = 0 # FrozenError!
455
456        owned = value.object
457        print(owned) # 5
458
459        # Create a copy of object.
460        clone = value.copy()
461        clone = 0  # Thats fine.
462        print(clone == owned) # False, 0 != 5
463
464        # None object.
465        value: Option[int] = Some(None)
466        print(value.as_ref())
467        # Some(None)
468        ```
469
470        Raises
471        ------
472        `dataclasses.FrozenInstanceError`
473            When attempting to modify the contained value. Use `sain.AsRef.copy()` method to create a copy.
474
475            Or just use `.as_mut()` if you're dealing with mutable objects.
476        """
477        if self._value is not None:
478            return Some(cell.Cell(self._value))
479
480        # SAFETY: self._value is None.
481        return NOTHING  # pyright: ignore

Returns immutable Some[sain.cell.Cell[T]] if the contained value is not None,

Otherwise returns Some(None).

Example
value = Some(5).as_ref().unwrap()
value.object = 0 # FrozenError!

owned = value.object
print(owned) # 5

# Create a copy of object.
clone = value.copy()
clone = 0  # Thats fine.
print(clone == owned) # False, 0 != 5

# None object.
value: Option[int] = Some(None)
print(value.as_ref())
# Some(None)
Raises
  • dataclasses.FrozenInstanceError: When attempting to modify the contained value. Use sain.AsRef.copy() method to create a copy.

Or just use .as_mut() if you're dealing with mutable objects.

def as_mut(self) -> Some[sain.cell.RefCell[~T]]:
483    def as_mut(self) -> Some[cell.RefCell[T]]:
484        """Returns mutable `Some[sain.cell.RefCell[T]]` if the contained value is not `None`,
485
486        Otherwise returns `Some(None)`.
487
488        Example
489        -------
490        ```py
491        value = Some(5).as_mut().unwrap()
492        value.object = 0
493        print(value) # Some(RefCell(0))
494
495        # None object.
496        value: Option[int] = Some(None)
497        print(value.as_mut())
498        # Some(None)
499        ```
500        """
501        if self._value is not None:
502            return Some(cell.RefCell(self._value))
503
504        # SAFETY: self._value is None.
505        return NOTHING  # pyright: ignore

Returns mutable Some[sain.cell.RefCell[T]] if the contained value is not None,

Otherwise returns Some(None).

Example
value = Some(5).as_mut().unwrap()
value.object = 0
print(value) # Some(RefCell(0))

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

class MessageIterator(sain.Iterator[Message]):
    def __init__(self, id: int = random.randint(0, 100)) -> None:
        self._session: requests.Session | None = None
        self.id = id

    def __next__(self) -> Message:
        if self._session is None:
            self._session = requests.session()

        try:
            with self._session:
                response = self._session.get(f"https://dummyjson.com/products/{self.id}").json()
        finally:
            self._session = None

        return Message(response["description"])

it = MessageIterator()
# Lazily fetch the first 5 messages from the API.
for msg in it.take(5):
    print(msg)
@staticmethod
@typing.final
def default() -> sain.iter.Empty[~Item]:
151    @staticmethod
152    @typing.final
153    def default() -> Empty[Item]:
154        """Return the default iterator for this type. It returns an empty iterator.
155
156        Example
157        -------
158        ```py
159        it: Iterator[int] = Iter.default()
160        assert t.next().is_none()
161        ```
162        """
163        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: collections.abc.Callable[[~Item], ~OtherItem] | None = None) -> collections.abc.Sequence[~Item] | collections.abc.Sequence[~OtherItem]:
175    @typing.final
176    def collect(
177        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
178    ) -> collections.Sequence[Item] | collections.Sequence[OtherItem]:
179        """Collects all items in the iterator into an immutable sequence.
180
181        Example
182        -------
183        ```py
184        iterator = Iter(range(3))
185        iterator.collect()
186        # (0, 1, 2, 3)
187        iterator.collect(cast=str) # Map each element and collect it.
188        # ('0', '1', '2', '3')
189        ```
190
191        Parameters
192        ----------
193        cast: `T | None`
194            An optional type to cast the items into.
195            If not provided the items will be returned as it's original type.
196        """
197        if cast is not None:
198            return tuple(cast(i) for i in self)
199
200        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 to_vec(self) -> Vec[~Item]:
202    @typing.final
203    def to_vec(self) -> vec.Vec[Item]:
204        """Convert this iterator into `Vec[T]`.
205
206        Example
207        -------
208        ```py
209        it = sain.iter.once(0)
210        vc = it.to_vec()
211
212        assert to_vec == [0]
213        ```
214        """
215        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:
217    @typing.final
218    def sink(self) -> None:
219        """Consume all elements from this iterator, flushing it into the sink.
220
221        Example
222        -------
223        ```py
224        it = Iter((1, 2, 3))
225        it.sink()
226        assert it.next().is_none()
227        ```
228        """
229        for _ in self:
230            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) -> collections.abc.Generator[~Item, None, None]:
232    @typing.final
233    def raw_parts(self) -> collections.Generator[Item, None, None]:
234        """Decompose all elements from this iterator, yielding it one by one
235        as a normal generator.
236
237        This mainly used for objects that needs to satisfy its exact type.
238
239        ```py
240        it = Iter("cba")
241        sort = sorted(it.raw_parts())
242
243        assert it.count() == 0
244        assert sort == ["a", "b", "c"]
245        ```
246        """
247        for item in self:
248            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]':
254    def next(self) -> Option[Item]:
255        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
256
257        Example
258        -------
259        ```py
260        iterator = Iter(["1", "2"])
261        assert iterator.next() == Some("1")
262        assert iterator.next() == Some("2")
263        assert iterator.next().is_none()
264        ```
265        """
266        try:
267            return _option.Some(self.__next__())
268        except StopIteration:
269            return _option.nothing_unchecked()

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]:
271    def cloned(self) -> Cloned[Item]:
272        """Creates an iterator which shallow copies its elements by reference.
273
274        If you need a copy of the actual iterator and not the elements.
275        use `Iter.clone()`
276
277        .. note::
278            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
279            on each item that is being yielded.
280
281        Example
282        -------
283        ```py
284        @dataclass
285        class User:
286            users_ids: list[int] = []
287
288        # An iterator which elements points to the same user.
289        user = User()
290        it = Iter((user, user))
291
292        for u in it.cloned():
293            u.user_ids.append(1)
294
295        # We iterated over the same user pointer twice and appended "1"
296        # since `copy` returns a shallow copy of nested structures.
297        assert len(user.user_ids) == 2
298        ```
299        """
300        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]:
302    def copied(self) -> Copied[Item]:
303        """Creates an iterator which copies all of its elements by value.
304
305        If you only need a copy of the item reference, Use `.cloned()` instead.
306
307        .. note::
308            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
309            on each item that is being yielded.
310
311        Example
312        -------
313        ```py
314        @dataclass
315        class User:
316            users_ids: list[int] = []
317
318        # An iterator which elements points to the same user.
319        user = User()
320        it = Iter((user, user))
321
322        for u in it.copied():
323            # A new list is created for each item.
324            u.user_ids.append(1)
325
326        # The actual list is untouched since we consumed a deep copy of it.
327        assert len(user.user_ids) == 0
328        ```
329        """
330        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: collections.abc.Callable[[~Item], ~OtherItem]) -> sain.iter.Map[~Item, ~OtherItem]:
332    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
333        """Maps each item in the iterator to another type.
334
335        Example
336        -------
337        ```py
338        iterator = Iter(["1", "2", "3"]).map(int)
339
340        for item in iterator:
341            assert isinstance(item, int)
342        ```
343
344        Parameters
345        ----------
346        predicate: `Callable[[Item], OtherItem]`
347            The function to map each item in the iterator to the other type.
348        """
349        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: collections.abc.Callable[[~Item], bool]) -> sain.iter.Filter[~Item]:
351    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
352        """Filters the iterator to only yield items that match the predicate.
353
354        Example
355        -------
356        ```py
357        places = Iter(['London', 'Paris', 'Los Angeles'])
358        for place in places.filter(lambda place: place.startswith('L')):
359            print(place)
360
361        # London
362        # Los Angeles
363        ```
364        """
365        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]:
367    def take(self, count: int) -> Take[Item]:
368        """Take the first number of items until the number of items
369        are yielded or the end of the iterator is exhausted.
370
371        Example
372        -------
373        ```py
374        iterator = Iter(['c', 'x', 'y'])
375
376        for x in iterator.take(2):
377            assert x in ('c', 'x')
378
379        # <Iter(['c', 'x'])>
380        ```
381        """
382        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]:
384    def skip(self, count: int) -> Skip[Item]:
385        """Skips the first number of items in the iterator.
386
387        Example
388        -------
389        ```py
390        iterator = Iter((1, 2, 3, 4))
391        for i in iterator.skip(2):
392            print(i)
393
394        # 3
395        # 4
396        ```
397        """
398        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]:
400    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
401        """Create a new iterator that yields a tuple of the index and item.
402
403        Example
404        -------
405        ```py
406        iterator = Iter([1, 2, 3])
407        for index, item in iterator.enumerate():
408            print(index, item)
409
410        # 0 1
411        # 1 2
412        # 2 3
413        ```
414        """
415        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: collections.abc.Callable[[~Item], bool]) -> sain.iter.TakeWhile[~Item]:
417    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
418        """yields items from the iterator while predicate returns `True`.
419
420        The rest of the items are discarded as soon as the predicate returns `False`
421
422        Example
423        -------
424        ```py
425        iterator = Iter(['a', 'ab', 'xd', 'ba'])
426        for x in iterator.take_while(lambda x: 'a' in x):
427            print(x)
428
429        # a
430        # ab
431        ```
432
433        Parameters
434        ----------
435        predicate: `collections.Callable[[Item], bool]`
436            The function to predicate each item in the iterator.
437        """
438        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: collections.abc.Callable[[~Item], bool]) -> sain.iter.DropWhile[~Item]:
440    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
441        """Yields items from the iterator while predicate returns `False`.
442
443        Example
444        -------
445        ```py
446        iterator = Iter(['a', 'ab', 'xd', 'ba'])
447        for x in iterator.drop_while(lambda x: 'a' in x):
448            print(x)
449
450        # xd
451        # ba
452        ```
453
454        Parameters
455        ----------
456        predicate: `collections.Callable[[Item], bool]`
457            The function to predicate each item in the iterator.
458        """
459        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 all(self, predicate: collections.abc.Callable[[~Item], bool]) -> bool:
461    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
462        """Return `True` if all items in the iterator match the predicate.
463
464        Example
465        -------
466        ```py
467        iterator = Iter([1, 2, 3])
468        while iterator.all(lambda item: isinstance(item, int)):
469            print("Still all integers")
470            continue
471            # Still all integers
472        ```
473
474        Parameters
475        ----------
476        predicate: `collections.Callable[[Item], bool]`
477            The function to test each item in the iterator.
478        """
479        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: collections.abc.Callable[[~Item], bool]) -> bool:
481    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
482        """`True` if any items in the iterator match the predicate.
483
484        Example
485        -------
486        ```py
487        iterator = Iter([1, 2, 3])
488        if iterator.any(lambda item: isinstance(item, int)):
489            print("At least one item is an int.")
490        # At least one item is an int.
491        ```
492
493        Parameters
494        ----------
495        predicate: `collections.Callable[[Item], bool]`
496            The function to test each item in the iterator.
497        """
498        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: collections.abc.Iterable[~OtherItem]) -> Iterator[tuple[~Item, ~OtherItem]]:
500    def zip(
501        self, other: collections.Iterable[OtherItem]
502    ) -> Iterator[tuple[Item, OtherItem]]:
503        """Zips the iterator with another iterable.
504
505        Example
506        -------
507        ```py
508        iterator = Iter([1, 2, 3])
509        for item, other_item in iterator.zip([4, 5, 6]):
510            assert item == other_item
511        <Iter([(1, 4), (2, 5), (3, 6)])>
512        ```
513
514        Parameters
515        ----------
516        other: `Iter[OtherItem]`
517            The iterable to zip with.
518
519        Returns
520        -------
521        `Iter[tuple[Item, OtherItem]]`
522            The zipped iterator.
523
524        """
525        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]:
527    def sort(
528        self,
529        *,
530        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
531        reverse: bool = False,
532    ) -> Iterator[Item]:
533        """Sorts the iterator.
534
535        Example
536        -------
537        ```py
538        iterator = Iter([3, 1, 6, 7])
539        for item in iterator.sort(key=lambda item: item < 3):
540            print(item)
541        # 1
542        # 3
543        # 6
544        # 7
545        ```
546
547        Parameters
548        ----------
549        key: `collections.Callable[[Item], Any]`
550            The function to sort by.
551        reverse: `bool`
552            Whether to reverse the sort.
553
554        """
555        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]:
557    def reversed(self) -> Iterator[Item]:
558        """Returns a new iterator that yields the items in the iterator in reverse order.
559
560        Example
561        -------
562        ```py
563        iterator = Iter([3, 1, 6, 7])
564        for item in iterator.reversed():
565            print(item)
566        # 7
567        # 6
568        # 1
569        # 3
570        ```
571        """
572        # NOTE: In order to reverse the iterator we need to
573        # first collect it into some collection.
574        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: collections.abc.Iterable[~Item]) -> Iterator[~Item]:
576    def union(self, other: collections.Iterable[Item]) -> Iterator[Item]:
577        """Returns a new iterator that yields all items from both iterators.
578
579        Example
580        -------
581        ```py
582        iterator = Iter([1, 2, 3])
583        other = [4, 5, 6]
584
585        for item in iterator.union(other):
586            print(item)
587        # 1
588        # 2
589        # 3
590        # 4
591        # 5
592        # 6
593        ```
594
595        Parameters
596        ----------
597        other: `Iter[Item]`
598            The iterable to union with.
599        """
600        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]':
602    def first(self) -> Option[Item]:
603        """Returns the first item in the iterator.
604
605        Example
606        -------
607        ```py
608        iterator = Iter([3, 1, 6, 7])
609        iterator.first().is_some_and(lambda x: x == 3)
610        ```
611        """
612        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]':
614    def last(self) -> Option[Item]:
615        """Returns the last item in the iterator.
616
617        Example
618        -------
619        ```py
620        iterator = Iter([3, 1, 6, 7])
621        iterator.last().is_some_and(lambda x: x == 7)
622        ```
623        """
624        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:
626    def count(self) -> int:
627        """Return the count of elements in memory this iterator has.
628
629        Example
630        -------
631        ```py
632        it = Iter(range(3))
633        assert it.count() == 3
634        ```
635        """
636        count = 0
637        for _ in self:
638            count += 1
639
640        return count

Return the count of elements in memory this iterator has.

Example
it = Iter(range(3))
assert it.count() == 3
def for_each(self, func: collections.abc.Callable[[~Item], typing.Any]) -> None:
642    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
643        """Calls `func` on each item in the iterator.
644
645        Example
646        -------
647        ```py
648        iterator = Iter([1, 2, 3])
649        iterator.for_each(lambda item: print(item))
650        # 1
651        # 2
652        # 3
653        ```
654
655        Parameters
656        ----------
657        func: `collections.Callable[[Item], typing.Any]`
658            The function to call on each item in the iterator.
659        """
660        for item in self:
661            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: collections.abc.Callable[[~Item], collections.abc.Coroutine[None, typing.Any, ~OtherItem]]) -> Union[Ok[collections.abc.Sequence[~OtherItem]], Err[sain.futures.SpawnError]]:
663    async def async_for_each(
664        self,
665        func: collections.Callable[
666            [Item], collections.Coroutine[None, typing.Any, OtherItem]
667        ],
668    ) -> _result.Result[collections.Sequence[OtherItem], futures.SpawnError]:
669        """Calls the async function on each item in the iterator concurrently.
670
671        Example
672        -------
673        ```py
674        async def create_user(username: str) -> None:
675            async with aiohttp.request("POST", f'.../{username}') as r:
676                return await r.json()
677
678        async def main():
679            users = sain.into_iter(["danny", "legalia"])
680            results = await users.async_for_each(lambda username: create_user(username))
681            for k, v in results.unwrap().items():
682                ...
683        ```
684
685        Parameters
686        ----------
687        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
688            The async function to call on each item in the iterator.
689        """
690        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.
def todo(message: Optional[LiteralString] = None) -> NoReturn:
231def todo(message: typing.LiteralString | None = None) -> typing.NoReturn:
232    """A place holder that indicates unfinished code.
233
234    Example
235    -------
236    ```py
237    from sain import todo
238
239    def from_json(payload: dict[str, int]) -> int:
240        # Calling this function will raise `Error`.
241        todo()
242    ```
243
244    Parameters
245    ----------
246    message : `str | None`
247        Multiple optional arguments to pass if the error was raised.
248    """
249    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.
def deprecated( *, since: Optional[LiteralString] = None, removed_in: Optional[LiteralString] = None, use_instead: Optional[LiteralString] = None, hint: Optional[LiteralString] = None) -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
152def deprecated(
153    *,
154    since: typing.LiteralString | None = None,
155    removed_in: typing.LiteralString | None = None,
156    use_instead: typing.LiteralString | None = None,
157    hint: typing.LiteralString | None = None,
158) -> collections.Callable[
159    [collections.Callable[P, U]],
160    collections.Callable[P, U],
161]:
162    """A decorator that marks a function as deprecated.
163
164    An attempt to call the object that's marked will cause a runtime warn.
165
166    Example
167    -------
168    ```py
169    from sain import deprecated
170
171    @deprecated(
172        since = "1.0.0",
173        removed_in ="3.0.0",
174        use_instead = "UserImpl()",
175        hint = "Hint for ux."
176    )
177    class User:
178        ...
179
180    user = User() # This will cause a warning at runtime.
181    ```
182
183    Parameters
184    ----------
185    since : `str`
186        The version that the function was deprecated.
187    removed_in : `str | None`
188        If provided, It will log when will the object will be removed in.
189    use_instead : `str | None`
190        If provided, This should be the alternative object name that should be used instead.
191    hint: `str`
192        An optional hint for the user.
193    """
194
195    def _create_message(obj: typing.Any) -> str:
196        msg = f"{_obj_type(obj)} `{obj.__module__}.{obj.__name__}` is deprecated."
197
198        if since is not None:
199            msg += f" since {since}."
200
201        if removed_in:
202            msg += f" Scheduled for removal in `{removed_in}`."
203
204        if use_instead is not None:
205            msg += f" Use `{use_instead}` instead."
206
207        if hint:
208            msg += f" Hint: {hint}"
209        return msg
210
211    def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]:
212        message = _create_message(func)
213
214        @functools.wraps(func)
215        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
216            _warn(message)
217            return func(*args, **kwargs)
218
219        # idk why pyright doesn't know the type of wrapper.
220        m = f"\n# Warning ⚠️\n{message}."
221        if wrapper.__doc__:
222            # append this message to an existing document.
223            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}"
224        else:
225            wrapper.__doc__ = m
226        return wrapper
227
228    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.
  • 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.
def unimplemented( *, message: Optional[LiteralString] = None, available_in: Optional[LiteralString] = None) -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
252def unimplemented(
253    *,
254    message: typing.LiteralString | None = None,
255    available_in: typing.LiteralString | None = None,
256) -> collections.Callable[
257    [collections.Callable[P, U]],
258    collections.Callable[P, U],
259]:
260    """A decorator that marks an object as unimplemented.
261
262    An attempt to call the object that's marked will cause a runtime warn.
263
264    Example
265    -------
266    ```py
267    from sain import unimplemented
268
269    @unimplemented("User object is not implemented yet.")
270    class User:
271        ...
272    ```
273
274    Parameters
275    ----------
276    message : `str | None`
277        An optional message to be displayed when the function is called. Otherwise default message will be used.
278    available_in : `str | None`
279        If provided, This will be shown as what release this object be implemented.
280    """
281
282    def _create_message(obj: typing.Any) -> str:
283        msg = (
284            message
285            or f"{_obj_type(obj)} `{obj.__module__}.{obj.__name__}` is not yet implemented."
286        )  # noqa: W503
287
288        if available_in:
289            msg += f" Available in `{available_in}`."
290        return msg
291
292    def decorator(obj: collections.Callable[P, U]) -> collections.Callable[P, U]:
293        message = _create_message(obj)
294
295        @functools.wraps(obj)
296        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
297            _warn(message)
298            return obj(*args, **kwargs)
299
300        # idk why pyright doesn't know the type of wrapper.
301        m = f"\n# Warning ⚠️\n{message}."
302        if wrapper.__doc__:
303            # append this message to an existing document.
304            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}"
305        else:
306            wrapper.__doc__ = m
307        return wrapper
308
309    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.
def doc( path: 'Read') -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
312def doc(
313    path: Read,
314) -> collections.Callable[
315    [collections.Callable[P, U]],
316    collections.Callable[P, U],
317]:
318    """Set `path` to be the object's documentation.
319
320    Example
321    -------
322    ```py
323    from sain import doc
324    from pathlib import Path
325
326    @doc(Path("../README.md"))
327    class User:
328
329        @doc("bool.html")
330        def bool_docs() -> None:
331            ...
332    ```
333
334    Parameters
335    ----------
336    path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]`
337        The path to read the content from.
338    """
339
340    def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]:
341        with open(path, "r") as file:
342            f.__doc__ = file.read()
343
344        @functools.wraps(f)
345        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
346            return f(*args, **kwargs)
347
348        return wrapper
349
350    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.
@typing.final
@dataclasses.dataclass(slots=True, frozen=True, repr=False)
class Ok(typing.Generic[~T]):
127@typing.final
128@dataclasses.dataclass(slots=True, frozen=True, repr=False)
129class Ok(typing.Generic[T]):
130    """Contains the success value of `Result[T, ...]`."""
131
132    _inner: T
133
134    ###############################
135    # * Boolean operations. * #
136    ###############################
137
138    def is_ok(self) -> typing.Literal[True]:
139        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
140
141        Example
142        -------
143        ```py
144        value: Result[str, None] = Ok("value")
145        assert value.is_ok() == True
146        ```
147        """
148        return True
149
150    def is_ok_and(self, f: F[T, bool]) -> bool:
151        """Returns `True` if the contained value is `Ok` and `f()` returns True..
152
153        Example
154        -------
155        ```py
156        value: Result[str, None] = Ok("value")
157
158        assert value.is_ok_and(lambda inner: inner == "value")
159        # True
160        ```
161        """
162        return self.is_ok() and f(self._inner)
163
164    # These are never truthy in an `Ok` instance.
165    def is_err(self) -> typing.Literal[False]:
166        """Returns `True` if the contained value is `Err`.
167
168        Example
169        -------
170        ```py
171        value: Result[str, None] = Ok("value")
172
173        assert value.is_err() == False
174        ```
175        """
176        return False
177
178    def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]:
179        """Returns `True` if the contained value is `Ok` and `f()` returns True..
180
181        Example
182        -------
183        ```py
184        value: Result[str, None] = Ok("value")
185
186        assert value.is_err_and(lambda inner: inner == "value")
187        # False
188        ```
189        """
190        return False
191
192    ###################
193    # * Extractors. * #
194    ###################
195
196    def expect(self, message: str, /) -> T:
197        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
198        if it was `Err` with `message` passed to it.
199
200        Example
201        -------
202        ```py
203        ok: Result[str, None] = Ok("owo")
204        ok.expect("err") # owo
205
206        err: Result[str, None] = Err(None)
207        err.expect("err") # RuntimeError("err")
208        ```
209        """
210        return self._inner
211
212    def unwrap(self) -> T:
213        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
214
215        Example
216        -------
217        ```py
218        ok: Result[str, None] = Ok("owo")
219        ok.unwrap() # owo
220
221        err: Result[str, None] = Err(None)
222        err.unwrap() # RuntimeError
223        ```
224        """
225        return self._inner
226
227    def unwrap_or(self, default: T, /) -> T:
228        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
229
230        Example
231        -------
232        ```py
233        ok: Result[str, None] = Ok("OwO")
234        ok.unwrap_or("uwu") # OwO
235
236        err: Result[str, None] = Err(None)
237        err.unwrap_or("uwu") # uwu
238        ```
239        """
240        return self._inner
241
242    def unwrap_or_else(self, f: F[E, T]) -> T:
243        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
244
245        Example
246        -------
247        ```py
248        ok: Result[int, str] = Ok(4)
249        ok.unwrap_or_else(lambda e: 0) # 4
250
251        err: Result[int, str] = Err("word")
252        err.unwrap_or_else(lambda e: len(e)) # 4
253        ```
254        """
255        return self._inner
256
257    def unwrap_err(self) -> typing.NoReturn:
258        """Return the contained `Err` value, Raising if it was `Ok`.
259
260        Example
261        -------
262        ```py
263        ok: Result[str, None] = Ok("buh")
264        ok.unwrap_err()  # RuntimeError
265
266        err: Result[str, None] = Err(None)
267        err.unwrap_err() == None
268        # True
269        ```
270        """
271        raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}")
272
273    ############################
274    # * Conversion adapters. * #
275    ############################
276
277    def ok(self) -> Option[T]:
278        """Convert `Ok[T]` to `Option[T]` if the contained value was `Ok` and `Option[None]` if it was `Err`.
279
280        Example
281        -------
282        ```py
283        value: Result[str, None] = Ok("buh")
284        value.ok().is_some() # True
285
286        value: Result[str, int] = Err(0)
287        value.ok().is_none() # True
288        ```
289        """
290        return _option.Some(self._inner)
291
292    def err(self) -> Option[None]:
293        """Convert `Err[T]` to `Option[T]` if the contained value was `Err` and `Option[None]` if it was `Ok`.
294
295        Example
296        -------
297        ```py
298        value: Result[str, None] = Ok("buh")
299        value.err().is_none() # True
300
301        value: Result[str, int] = Err(0)
302        value.err().is_some() # True
303        ```
304        """
305        return _option.NOTHING
306
307    def inspect(self, f: F[T, typing.Any]) -> None:
308        """Call a function to the contained value if it was `Ok` and do nothing if it was `Err`
309
310        Example
311        -------
312        ```py
313        def sink(value: str) -> None:
314            # do something with value
315            print("Called " + value)
316
317        x: Result[str, None] = Ok("ok")
318        x.inspect(sink) # "Called ok"
319
320        x: Result[str, str] = Err("err")
321        x.inspect(sink) # None
322        ```
323        """
324        f(self._inner)
325
326    def inspect_err(self, f: F[E, typing.Any]) -> None:
327        """Call a function to the contained value if it was `Err` and do nothing if it was `Ok`
328
329        Example
330        -------
331        ```py
332        def sink(value: str) -> None:
333            # do something with value
334            print("Called " + value)
335
336        x: Result[str, None] = Ok("ok")
337        x.inspect_err(sink) # None
338
339        x: Result[str, str] = Err("err")
340        x.inspect_err(sink) # Called err
341        ```
342        """
343
344    def map(self, f: F[T, U], /) -> Ok[U]:
345        """Map `Ok[T]` to `Ok[U]` by applying a function to `T`, Leaving `Err` untouched.
346
347        Example
348        -------
349        ```py
350        ok: Result[str, int] = Ok("1")
351        ok.map(lambda c: int(c) + 1) # Ok(2)
352
353        err: Result[str, int] = Err(0)
354        err.map(str.upper) # Err(0)
355        ```
356        """
357        return Ok(f(self._inner))
358
359    def map_or(self, f: F[T, U], default: U, /) -> U:
360        """Returns the provided default value if `Err`,
361
362        Otherwise extracts the `Ok` value and maps it to `f()`
363
364        Example
365        -------
366        ```py
367        x: Result[str, str] = Ok("foo")
368        assert x.map_or(lambda c: len(c), 42) == 3
369
370        x: Result[str, str] = Err("bar")
371        assert x.map_or(lambda c: len(c), 42) == 42
372        ```
373        """
374        return f(self._inner)
375
376    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
377        """Returns the provided default value from a function if `Err`,
378
379        Otherwise extracts the `Ok` value and maps it to `f()`
380
381        Example
382        -------
383        ```py
384        x: Result[str, str] = Ok("four")
385        assert x.map_or_else(
386            lambda c: 2 * len(c),
387            lambda err: len(err)
388        ) == 8
389
390        x: Result[str, str] = Err("bar")
391        assert x.map_or_else(
392            lambda c: 2 * len(c),
393            lambda err: len(err)
394        ) == 3
395        ```
396        """
397        return f(self._inner)
398
399    def map_err(self, f: F[E, U], /) -> Self:
400        """Maps a `Result[T, E]` to `Result[T, U]`, leaving `OK[T]` untouched.
401
402        Example
403        -------
404        ```py
405        x: Result[str, int] = Ok("blue")
406        x.map_err(lambda err: err + 1) # Ok("blue")
407
408        x: Result[str, int] = Err(5)
409        x.map_err(float) # Err(5.0)
410        ```
411        """
412        return self
413
414    ##############################
415    # * Iterator constructors. * #
416    ##############################
417
418    def iter(self) -> _iter.Iterator[T]:
419        """Return an iterator over the contained value.
420
421        If it was `Ok[T]` then it will return `Iter[T]`, otherwise it will return `Iter[Never]`.
422
423        Example
424        -------
425        ```py
426        c: Result[str, int] = Ok("blue")
427        c.iter().next() == Some("blue")
428
429        c: Result[str, int] = Err(0)
430        c.iter().next() == Some(None)
431        ```
432        """
433        return self.__iter__()
434
435    def __iter__(self) -> _iter.Iterator[T]:
436        return _iter.once(self._inner)
437
438    #################
439    # * Overloads * #
440    #################
441
442    def __repr__(self) -> str:
443        return f"Ok({self._inner})"
444
445    def __or__(self, other: T) -> T:
446        return self._inner
447
448    def __invert__(self) -> T:
449        return self._inner

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

Ok(_inner: ~T)
def is_ok(self) -> Literal[True]:
138    def is_ok(self) -> typing.Literal[True]:
139        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
140
141        Example
142        -------
143        ```py
144        value: Result[str, None] = Ok("value")
145        assert value.is_ok() == True
146        ```
147        """
148        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: collections.abc.Callable[[~T], bool]) -> bool:
150    def is_ok_and(self, f: F[T, bool]) -> bool:
151        """Returns `True` if the contained value is `Ok` and `f()` returns True..
152
153        Example
154        -------
155        ```py
156        value: Result[str, None] = Ok("value")
157
158        assert value.is_ok_and(lambda inner: inner == "value")
159        # True
160        ```
161        """
162        return self.is_ok() and 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]:
165    def is_err(self) -> typing.Literal[False]:
166        """Returns `True` if the contained value is `Err`.
167
168        Example
169        -------
170        ```py
171        value: Result[str, None] = Ok("value")
172
173        assert value.is_err() == False
174        ```
175        """
176        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: collections.abc.Callable[[~T], bool]) -> Literal[False]:
178    def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]:
179        """Returns `True` if the contained value is `Ok` and `f()` returns True..
180
181        Example
182        -------
183        ```py
184        value: Result[str, None] = Ok("value")
185
186        assert value.is_err_and(lambda inner: inner == "value")
187        # False
188        ```
189        """
190        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:
196    def expect(self, message: str, /) -> T:
197        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
198        if it was `Err` with `message` passed to it.
199
200        Example
201        -------
202        ```py
203        ok: Result[str, None] = Ok("owo")
204        ok.expect("err") # owo
205
206        err: Result[str, None] = Err(None)
207        err.expect("err") # RuntimeError("err")
208        ```
209        """
210        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 unwrap(self) -> ~T:
212    def unwrap(self) -> T:
213        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
214
215        Example
216        -------
217        ```py
218        ok: Result[str, None] = Ok("owo")
219        ok.unwrap() # owo
220
221        err: Result[str, None] = Err(None)
222        err.unwrap() # RuntimeError
223        ```
224        """
225        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:
227    def unwrap_or(self, default: T, /) -> T:
228        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
229
230        Example
231        -------
232        ```py
233        ok: Result[str, None] = Ok("OwO")
234        ok.unwrap_or("uwu") # OwO
235
236        err: Result[str, None] = Err(None)
237        err.unwrap_or("uwu") # uwu
238        ```
239        """
240        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: collections.abc.Callable[[~E], ~T]) -> ~T:
242    def unwrap_or_else(self, f: F[E, T]) -> T:
243        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
244
245        Example
246        -------
247        ```py
248        ok: Result[int, str] = Ok(4)
249        ok.unwrap_or_else(lambda e: 0) # 4
250
251        err: Result[int, str] = Err("word")
252        err.unwrap_or_else(lambda e: len(e)) # 4
253        ```
254        """
255        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:
257    def unwrap_err(self) -> typing.NoReturn:
258        """Return the contained `Err` value, Raising if it was `Ok`.
259
260        Example
261        -------
262        ```py
263        ok: Result[str, None] = Ok("buh")
264        ok.unwrap_err()  # RuntimeError
265
266        err: Result[str, None] = Err(None)
267        err.unwrap_err() == None
268        # True
269        ```
270        """
271        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) -> Some[~T]:
277    def ok(self) -> Option[T]:
278        """Convert `Ok[T]` to `Option[T]` if the contained value was `Ok` and `Option[None]` if it was `Err`.
279
280        Example
281        -------
282        ```py
283        value: Result[str, None] = Ok("buh")
284        value.ok().is_some() # True
285
286        value: Result[str, int] = Err(0)
287        value.ok().is_none() # True
288        ```
289        """
290        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) -> Some[NoneType]:
292    def err(self) -> Option[None]:
293        """Convert `Err[T]` to `Option[T]` if the contained value was `Err` and `Option[None]` if it was `Ok`.
294
295        Example
296        -------
297        ```py
298        value: Result[str, None] = Ok("buh")
299        value.err().is_none() # True
300
301        value: Result[str, int] = Err(0)
302        value.err().is_some() # True
303        ```
304        """
305        return _option.NOTHING

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: collections.abc.Callable[[~T], typing.Any]) -> None:
307    def inspect(self, f: F[T, typing.Any]) -> None:
308        """Call a function to the contained value if it was `Ok` and do nothing if it was `Err`
309
310        Example
311        -------
312        ```py
313        def sink(value: str) -> None:
314            # do something with value
315            print("Called " + value)
316
317        x: Result[str, None] = Ok("ok")
318        x.inspect(sink) # "Called ok"
319
320        x: Result[str, str] = Err("err")
321        x.inspect(sink) # None
322        ```
323        """
324        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: collections.abc.Callable[[~E], typing.Any]) -> None:
326    def inspect_err(self, f: F[E, typing.Any]) -> None:
327        """Call a function to the contained value if it was `Err` and do nothing if it was `Ok`
328
329        Example
330        -------
331        ```py
332        def sink(value: str) -> None:
333            # do something with value
334            print("Called " + value)
335
336        x: Result[str, None] = Ok("ok")
337        x.inspect_err(sink) # None
338
339        x: Result[str, str] = Err("err")
340        x.inspect_err(sink) # Called err
341        ```
342        """

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: collections.abc.Callable[[~T], ~U], /) -> Ok[~U]:
344    def map(self, f: F[T, U], /) -> Ok[U]:
345        """Map `Ok[T]` to `Ok[U]` by applying a function to `T`, Leaving `Err` untouched.
346
347        Example
348        -------
349        ```py
350        ok: Result[str, int] = Ok("1")
351        ok.map(lambda c: int(c) + 1) # Ok(2)
352
353        err: Result[str, int] = Err(0)
354        err.map(str.upper) # Err(0)
355        ```
356        """
357        return Ok(f(self._inner))

Map Ok[T] to Ok[U] by applying a function to T, 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: collections.abc.Callable[[~T], ~U], default: ~U, /) -> ~U:
359    def map_or(self, f: F[T, U], default: U, /) -> U:
360        """Returns the provided default value if `Err`,
361
362        Otherwise extracts the `Ok` value and maps it to `f()`
363
364        Example
365        -------
366        ```py
367        x: Result[str, str] = Ok("foo")
368        assert x.map_or(lambda c: len(c), 42) == 3
369
370        x: Result[str, str] = Err("bar")
371        assert x.map_or(lambda c: len(c), 42) == 42
372        ```
373        """
374        return f(self._inner)

Returns the provided default value if 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: collections.abc.Callable[[~T], ~U], default: collections.abc.Callable[[~E], ~U], /) -> ~U:
376    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
377        """Returns the provided default value from a function if `Err`,
378
379        Otherwise extracts the `Ok` value and maps it to `f()`
380
381        Example
382        -------
383        ```py
384        x: Result[str, str] = Ok("four")
385        assert x.map_or_else(
386            lambda c: 2 * len(c),
387            lambda err: len(err)
388        ) == 8
389
390        x: Result[str, str] = Err("bar")
391        assert x.map_or_else(
392            lambda c: 2 * len(c),
393            lambda err: len(err)
394        ) == 3
395        ```
396        """
397        return f(self._inner)

Returns the provided default value from a function if Err,

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

Example
x: Result[str, str] = Ok("four")
assert x.map_or_else(
    lambda c: 2 * len(c),
    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: collections.abc.Callable[[~E], ~U], /) -> Self:
399    def map_err(self, f: F[E, U], /) -> Self:
400        """Maps a `Result[T, E]` to `Result[T, U]`, leaving `OK[T]` untouched.
401
402        Example
403        -------
404        ```py
405        x: Result[str, int] = Ok("blue")
406        x.map_err(lambda err: err + 1) # Ok("blue")
407
408        x: Result[str, int] = Err(5)
409        x.map_err(float) # Err(5.0)
410        ```
411        """
412        return self

Maps a Result[T, E] to Result[T, U], leaving OK[T] 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]:
418    def iter(self) -> _iter.Iterator[T]:
419        """Return an iterator over the contained value.
420
421        If it was `Ok[T]` then it will return `Iter[T]`, otherwise it will return `Iter[Never]`.
422
423        Example
424        -------
425        ```py
426        c: Result[str, int] = Ok("blue")
427        c.iter().next() == Some("blue")
428
429        c: Result[str, int] = Err(0)
430        c.iter().next() == Some(None)
431        ```
432        """
433        return self.__iter__()

Return an iterator over the contained value.

If it was Ok[T] then it will return Iter[T], otherwise it will return Iter[Never].

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]):
452@typing.final
453@dataclasses.dataclass(slots=True, frozen=True, repr=False)
454class Err(typing.Generic[E]):
455    """Contains the error value of `Result[..., E]`."""
456
457    _inner: E
458
459    ################################
460    # * Boolean operations. * #
461    ################################
462
463    def is_ok(self) -> typing.Literal[False]:
464        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
465
466        Example
467        -------
468        ```py
469        value: Result[str, None] = Err(None)
470
471        assert value.is_ok() == False
472        ```
473        """
474        return False
475
476    def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]:
477        """Returns `True` if the contained value is `Ok` and `f()` returns True.
478
479        Example
480        -------
481        ```py
482        value: Result[str, None] = Err(None)
483
484        assert value.is_ok_and(lambda inner: inner == "value")
485        # False
486        ```
487        """
488        return False
489
490    # These are never truthy in an `Ok` instance.
491    def is_err(self) -> typing.Literal[True]:
492        """Returns `True` if the contained value is `Err`.
493
494        Example
495        -------
496        ```py
497        value: Result[str, None] = Err(None)
498
499        assert value.is_err() == True
500        ```
501        """
502        return True
503
504    def is_err_and(self, f: F[E, bool]) -> bool:
505        """Returns `True` if the contained value is `Ok` and `f()` returns True..
506
507        Example
508        -------
509        ```py
510        value: Result[str, None] = Err(None)
511
512        assert value.is_err_and(lambda err: err is None)
513        # True
514        ```
515        """
516        return self.is_err() and f(self._inner)
517
518    ###################
519    # * Extractors. * #
520    ###################
521
522    def expect(self, msg: str) -> typing.NoReturn:
523        raise RuntimeError(msg) from None
524
525    def expect_err(self) -> E:
526        return self._inner
527
528    def unwrap(self) -> typing.NoReturn:
529        raise RuntimeError(
530            f"Called `unwrap()` on an `Err` variant: {self._inner!r}"
531        ) from None
532
533    def unwrap_or(self, __default: T, /) -> T:
534        return __default
535
536    def unwrap_or_else(self, f: F[E, T]) -> T:
537        return f(self._inner)
538
539    def unwrap_err(self) -> E:
540        return self._inner
541
542    ############################
543    # * Conversion adapters. * #
544    ############################
545
546    def inspect(self, f: F[T, typing.Any]) -> None:
547        return None
548
549    def inspect_err(self, f: F[E, typing.Any]) -> None:
550        f(self._inner)
551
552    def ok(self) -> Option[None]:
553        return _option.NOTHING
554
555    def err(self) -> Option[E]:
556        return _option.Some(self._inner)
557
558    def map(self, f: F[E, U]) -> Self:
559        return self
560
561    def map_or(self, f: F[E, U], default: U, /) -> U:
562        return default
563
564    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
565        return default(self._inner)
566
567    def map_err(self, f: F[E, U]) -> Err[U]:
568        return Err(f(self._inner))
569
570    ##############################
571    # * Iterator constructors. * #
572    ##############################
573
574    def iter(self) -> _iter.Iterator[Never]:
575        return self.__iter__()
576
577    def __iter__(self) -> _iter.Iterator[Never]:
578        return _iter.empty()
579
580    #################
581    # * Overloads * #
582    #################
583
584    def __repr__(self) -> str:
585        return f"Err({self._inner})"
586
587    def __or__(self, other: T) -> T:
588        return other
589
590    def __invert__(self) -> typing.NoReturn:
591        self.unwrap()

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

Err(_inner: ~E)
def is_ok(self) -> Literal[False]:
463    def is_ok(self) -> typing.Literal[False]:
464        """Returns `True` if the contained value is `Ok` and `False` if it an `Err`.
465
466        Example
467        -------
468        ```py
469        value: Result[str, None] = Err(None)
470
471        assert value.is_ok() == False
472        ```
473        """
474        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: collections.abc.Callable[[~E], bool]) -> Literal[False]:
476    def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]:
477        """Returns `True` if the contained value is `Ok` and `f()` returns True.
478
479        Example
480        -------
481        ```py
482        value: Result[str, None] = Err(None)
483
484        assert value.is_ok_and(lambda inner: inner == "value")
485        # False
486        ```
487        """
488        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]:
491    def is_err(self) -> typing.Literal[True]:
492        """Returns `True` if the contained value is `Err`.
493
494        Example
495        -------
496        ```py
497        value: Result[str, None] = Err(None)
498
499        assert value.is_err() == True
500        ```
501        """
502        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: collections.abc.Callable[[~E], bool]) -> bool:
504    def is_err_and(self, f: F[E, bool]) -> bool:
505        """Returns `True` if the contained value is `Ok` and `f()` returns True..
506
507        Example
508        -------
509        ```py
510        value: Result[str, None] = Err(None)
511
512        assert value.is_err_and(lambda err: err is None)
513        # True
514        ```
515        """
516        return self.is_err() and 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:
522    def expect(self, msg: str) -> typing.NoReturn:
523        raise RuntimeError(msg) from None
def expect_err(self) -> ~E:
525    def expect_err(self) -> E:
526        return self._inner
def unwrap(self) -> NoReturn:
528    def unwrap(self) -> typing.NoReturn:
529        raise RuntimeError(
530            f"Called `unwrap()` on an `Err` variant: {self._inner!r}"
531        ) from None
def unwrap_or(self, _Err__default: ~T, /) -> ~T:
533    def unwrap_or(self, __default: T, /) -> T:
534        return __default
def unwrap_or_else(self, f: collections.abc.Callable[[~E], ~T]) -> ~T:
536    def unwrap_or_else(self, f: F[E, T]) -> T:
537        return f(self._inner)
def unwrap_err(self) -> ~E:
539    def unwrap_err(self) -> E:
540        return self._inner
def inspect(self, f: collections.abc.Callable[[~T], typing.Any]) -> None:
546    def inspect(self, f: F[T, typing.Any]) -> None:
547        return None
def inspect_err(self, f: collections.abc.Callable[[~E], typing.Any]) -> None:
549    def inspect_err(self, f: F[E, typing.Any]) -> None:
550        f(self._inner)
def ok(self) -> Some[NoneType]:
552    def ok(self) -> Option[None]:
553        return _option.NOTHING
def err(self) -> Some[~E]:
555    def err(self) -> Option[E]:
556        return _option.Some(self._inner)
def map(self, f: collections.abc.Callable[[~E], ~U]) -> Self:
558    def map(self, f: F[E, U]) -> Self:
559        return self
def map_or(self, f: collections.abc.Callable[[~E], ~U], default: ~U, /) -> ~U:
561    def map_or(self, f: F[E, U], default: U, /) -> U:
562        return default
def map_or_else( self, f: collections.abc.Callable[[~T], ~U], default: collections.abc.Callable[[~E], ~U], /) -> ~U:
564    def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U:
565        return default(self._inner)
def map_err(self, f: collections.abc.Callable[[~E], ~U]) -> Err[~U]:
567    def map_err(self, f: F[E, U]) -> Err[U]:
568        return Err(f(self._inner))
def iter(self) -> Iterator[typing.Never]:
574    def iter(self) -> _iter.Iterator[Never]:
575        return self.__iter__()
Result = typing.Union[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    Comparing different collections with `Vec` have a cost. Depending on what you're comparing it wit.
127
128    Any iterable that is not a `list` or `Vec` that is used to compare with will get copied into a `list`,
129    So be careful what you compare a `Vec` with.
130
131    ```py
132    vec = Vec((1,2,3))
133    # zero-cost
134    vec == [1, 2, 3]
135    # Copies {1, 2, 3} -> [1, 2, 3] which can cost.
136    vec == {1, 2, 3}
137    ```
138
139    Zero-Copy
140    ---------
141    A vec that gets initialized from a `list` will *point* to it and doesn't copy it.
142    So any element that gets appended to the collection will also get pushed into the vec.
143
144    ```py
145    cells: list[str] = []
146    vec = Vec(cells) # This DOES NOT copy the `cells`.
147
148    cells.append("foo")
149    vec[0] == "foo"  # True
150    ```
151
152    The opposite of the above is to initialize the vec from either
153    an iterable or args, or copy the list.
154
155    ```py
156    # Creates a new vec and extend it with the elements.
157    from_args = vec.from_args("foo", "bar")
158
159    # inlined from another iterable.
160    from_iter = Vec(["foo", "bar"])
161
162    # Copy the list into a vec.
163    vec = Vec(cells[:])
164    cells.append("bar")
165
166    vec[2] # IndexError: "bar" doesn't exist in vec.
167    ```
168    """
169
170    __slots__ = ("_ptr", "_capacity")
171
172    @typing.overload
173    def __init__(self) -> None: ...
174
175    @typing.overload
176    def __init__(self, iterable: collections.Iterable[T]) -> None: ...
177
178    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
179        # We won't allocate to build the list here.
180        # Instead, On first push or fist indexed set
181        # we allocate if it was None.
182        if isinstance(iterable, list):
183            # Calling `list()` on another list will copy it, So instead we just point to it.
184            self._ptr = iterable
185        elif isinstance(iterable, Vec):
186            self._ptr = iterable._ptr
187        # any other iterable that ain't a list needs to get copied into a new list.
188        else:
189            self._ptr: list[T] | None = list(iterable) if iterable else None
190
191        self._capacity: int | None = None
192
193    @classmethod
194    def with_capacity(cls, capacity: int) -> Self:
195        """Create a new `Vec` with at least the specified capacity.
196        This vec will be able to hold `capacity` elements without pushing further.
197
198        Check out `Vec.push_within_capacity` as well.
199
200        Example
201        -------
202        ```py
203        vec = Vec.with_capacity(3)
204        assert vec.len() == 0 and vec.capacity() >= 3
205
206        vec.push(1)
207        vec.push(2)
208        vec.push(3)
209        print(vec.len()) # 3
210
211        # This won't push.
212        vec.push(4)
213        ```
214        """
215        v = cls()
216        v._capacity = capacity
217        return v
218
219    def as_ref(self) -> collections.Collection[T]:
220        """Return an immutable view over this vector elements.
221
222        Example
223        -------
224        ```py
225        vec = Vec((1,2,3))
226
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().is_none()
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        iterator = vec.iter()
268
269        # Map each element to a str
270        for element in iterator.map(str):
271            print(element)
272        ```
273        """
274        return _iter.Iter(self)
275
276    def is_empty(self) -> bool:
277        """Returns true if the vector contains no elements."""
278        return not self._ptr
279
280    def split_off(self, at: int) -> Vec[T]:
281        """Split the vector off at the specified position.
282
283        if this vec is empty, `self` is returned unchanged.
284
285        Example
286        -------
287        ```py
288        vec = Vec((1, 2, 3))
289        split = vec.split_off(1)
290
291        print(split, vec)  # [1], [2, 3]
292        ```
293
294        Raises
295        ------
296        `RuntimeError`
297            This method will raise if `at` > `len(self)`
298        """
299        if at > self.len():
300            raise RuntimeError(
301                f"Index `at` ({at}) should be <= than len of vector ({self.len()}) "
302            ) from None
303
304        # Either the list is empty or uninit.
305        if not self._ptr:
306            return self
307
308        split = self[at : self.len()]
309        self._ptr = self._ptr[0:at]
310        return split
311
312    def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
313        """Split the first and rest elements of the vector, If empty, `Some[None]` is returned.
314
315        Example
316        -------
317        ```py
318        vec = Vec([1, 2, 3])
319        split = vec.split_first()
320        assert split == Some((1, [2, 3]))
321
322        vec: Vec[int] = Vec()
323        split = vec.split_first()
324        assert split == Some(None)
325        ```
326        """
327        if not self._ptr:
328            return _option.nothing_unchecked()
329
330        first, *rest = self._ptr
331        return _option.Some((first, rest))
332
333    def first(self) -> _option.Option[T]:
334        """Get the first element in this vec, returning `Some[None]` if there's none.
335
336        Example
337        -------
338        ```py
339        vec = Vec((1,2,3))
340        first = vec.first()
341        assert ~first == 1
342        ```
343        """
344        return self.get(0)
345
346    def truncate(self, size: int) -> None:
347        """Shortens the vec, keeping the first `size` elements and dropping the rest.
348
349        Example
350        -------
351        ```py
352        vec = Vec([1,2,3])
353        vec.truncate(1)
354        assert vec == [1]
355        ```
356        """
357        if not self._ptr:
358            return
359
360        self._ptr = self._ptr[:size]
361
362    def retain(self, f: collections.Callable[[T], bool]) -> None:
363        """Remove elements from this vec while `f()` returns `True`.
364
365        In other words, filter this vector based on `f()`.
366
367        Example
368        -------
369        ```py
370        vec = Vec([1, 2, 3])
371        vec.retain(lambda elem: elem > 1)
372
373        assert vec == [2, 3]
374        ```
375        """
376        if not self._ptr:
377            return
378
379        self._ptr = [e for e in self._ptr if f(e)]
380
381    def swap_remove(self, item: T) -> T:
382        """Remove the first appearance of `item` from this vector and return it.
383
384        Raises
385        ------
386        * `ValueError`: if `item` is not in this vector.
387        * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it.
388
389        Example
390        -------
391        ```py
392        vec = Vector(('a', 'b', 'c'))
393        element = vec.remove('a')
394        assert vec == ['b', 'c'] and element == 'a'
395        ```
396        """
397        if self._ptr is None:
398            raise MemoryError("Vec is unallocated.") from None
399
400        try:
401            i = next(i for i in self._ptr if i == item)
402        except StopIteration:
403            raise ValueError(f"Item `{item}` not in list") from None
404
405        self.remove(i)
406        return i
407
408    def push(self, item: T) -> None:
409        """Push an element at the end of the vector.
410
411        Example
412        -------
413        ```py
414        vec = Vec()
415        vec.push(1)
416
417        assert vec == [1]
418        ```
419        """
420        if self._capacity is not None:
421            self.push_within_capacity(item)
422            return
423
424        if self._ptr is None:
425            self._ptr = []
426
427        self._ptr.append(item)
428
429    def push_within_capacity(self, x: T) -> Result[None, T]:
430        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
431
432        Example
433        -------
434        ```py
435        vec: Vec[int] = Vec.with_capacity(3)
436        for i in range(3):
437            match vec.push_within_capacity(i):
438                case Ok(_):
439                    print("All good.")
440                case Err(elem):
441                    print("Reached max cap :< cant push", elem)
442        ```
443
444        Or you can also just call `Vec.push` and it will push within capacity if `Vec.capacity()` is not `None`.
445        ```py
446        vec: Vec[int] = Vec.with_capacity(3)
447
448        vec.extend((1, 2, 3))
449        vec.push(4)
450
451        assert vec.len() == 3
452        ```
453        """
454        if self._ptr is None:
455            self._ptr = []
456
457        if self._capacity is not None and self._capacity <= self.len():
458            return _result.Err(x)
459
460        self._ptr.append(x)
461        return _result.Ok(None)
462
463    def reserve(self, additional: int) -> None:
464        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
465
466        Example
467        -------
468        ```py
469        vec = Vec.with_capacity(3)
470        is_vip = random.choice((True, False))
471
472        for i in range(4):
473            match vec.push_within_capacity(i):
474                case Ok(_):
475                    print("All good")
476                case Err(person):
477                    # If the person is a VIP, then reserve for one more.
478                    if is_vip:
479                        vec.reserve(1)
480                        continue
481
482                    # is_vip was false.
483                    print("Can't reserve for non-VIP members...", person)
484                    break
485        ```
486        """
487        if self._capacity is not None:
488            self._capacity += additional
489
490    ##########################
491    # * Builtin Operations *
492    ##########################
493
494    # For people how are used to calling list.append
495    append = push
496    """An alias for `Vec.push` method."""
497
498    def get(self, index: int) -> _option.Option[T]:
499        """Get the item at the given index, or `Some[None]` if its out of bounds.
500
501        Example
502        -------
503        ```py
504        vec = Vec((1, 2, 3))
505        vec.get(0) == Some(1)
506        vec.get(3) == Some(None)
507        ```
508        """
509        try:
510            return _option.Some(self.__getitem__(index))
511        except IndexError:
512            return _option.nothing_unchecked()
513
514    def insert(self, index: int, value: T) -> None:
515        """Insert an element at the position `index`.
516
517        Example
518        --------
519        ```py
520        vec = Vec((2, 3))
521        vec.insert(0, 1)
522        assert vec == [1, 2, 3]
523        ```
524        """
525        self.__setitem__(index, value)
526
527    def pop(self, index: int = -1) -> _option.Option[T]:
528        """Removes the last element from a vector and returns it, or `sain.Some(None)` if it is empty.
529
530        Example
531        -------
532        ```py
533        vec = Vec((1, 2, 3))
534        assert vec.pop() == Some(3)
535        assert vec == [1, 2]
536        ```
537        """
538        if not self._ptr:
539            return _option.nothing_unchecked()
540
541        return _option.Some(self._ptr.pop(index))
542
543    def remove(self, item: T) -> None:
544        """Remove `item` from this vector.
545
546        Example
547        -------
548        ```py
549        vec = Vector(('a', 'b', 'c'))
550        vec.remove('a')
551        assert vec == ['b', 'c']
552        ```
553        """
554        if not self._ptr:
555            return
556
557        self._ptr.remove(item)
558
559    def extend(self, iterable: collections.Iterable[T]) -> None:
560        """Extend this vector from another iterable.
561
562        Example
563        -------
564        ```py
565        vec = Vec((1, 2, 3))
566        vec.extend((4, 5, 6))
567
568        assert vec == [1, 2, 3, 4, 5, 6]
569        ```
570        """
571        if self._ptr is None:
572            self._ptr = []
573
574        self._ptr.extend(iterable)
575
576    def copy(self) -> Vec[T]:
577        """Create a vector that copies all of its elements and place it into the new one.
578
579        If the vector hasn't been allocated, `self` is returned.
580
581        Example
582        -------
583        ```py
584        original = Vec((1,2,3))
585        copy = original.copy()
586        copy.push(4)
587
588        print(original) # [1, 2, 3]
589        ```
590        """
591        return Vec(self._ptr[:]) if self._ptr is not None else self
592
593    def clear(self) -> None:
594        """Clear all elements of this vector.
595
596        Example
597        -------
598        ```py
599        vec = Vec((1,2,3))
600        vec.clear()
601        assert vec.len() == 0
602        ```
603        """
604        if not self._ptr:
605            return
606
607        self._ptr.clear()
608
609    def sort(
610        self,
611        *,
612        key: collections.Callable[[T], SupportsRichComparison] | None = None,
613        reverse: bool = False,
614    ) -> None:
615        """This method sorts the list in place, using only < comparisons between items.
616
617        Example
618        -------
619        ```py
620        vec = Vec((2, 1, 3))
621        vec.sort()
622        assert vec == [1, 2, 3]
623        ```
624        """
625        if not self._ptr:
626            return
627
628        # key can be `None` here just fine, idk why pyright is complaining.
629        self._ptr.sort(key=key, reverse=reverse)  # pyright: ignore
630
631    def index(
632        self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize
633    ) -> int:
634        # << Official documentation >>
635        """Return zero-based index in the vec of the first item whose value is equal to `item`.
636        Raises a ValueError if there is no such item.
637
638        Example
639        -------
640        ```py
641        vec = Vec((1, 2, 3))
642        assert vec.index(2) == 1
643        ```
644        """
645        if self._ptr is None:
646            raise ValueError from None
647
648        return self._ptr.index(item, start, end)
649
650    def count(self, item: T) -> int:
651        """Return the number of occurrences of `item` in the vec.
652
653        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
654
655        Example
656        --------
657        ```py
658        vec = Vec((1, 2, 3, 3))
659        assert vec.count(3) == 2
660        ```
661        """
662        if self._ptr is None:
663            return 0
664
665        return self._ptr.count(item)
666
667    def __len__(self) -> int:
668        return len(self._ptr) if self._ptr else 0
669
670    def __setitem__(self, index: int, value: T):
671        if not self._ptr:
672            raise IndexError from None
673
674        self._ptr[index] = value
675
676    @typing.overload
677    def __getitem__(self, index: slice) -> Vec[T]: ...
678
679    @typing.overload
680    def __getitem__(self, index: int) -> T: ...
681
682    def __getitem__(self, index: int | slice) -> T | Vec[T]:
683        if not self._ptr:
684            raise IndexError("Index out of range")
685
686        if isinstance(index, slice):
687            return self.__class__(self._ptr[index])
688
689        return self._ptr[index]
690
691    def __delitem__(self, index: int) -> None:
692        if not self._ptr:
693            return
694
695        del self._ptr[index]
696
697    def __contains__(self, element: T) -> bool:
698        return element in self._ptr if self._ptr else False
699
700    def __iter__(self) -> collections.Iterator[T]:
701        if self._ptr is None:
702            return iter(())
703
704        return self._ptr.__iter__()
705
706    def __repr__(self) -> str:
707        return _LIST_REPR if not self._ptr else repr(self._ptr)
708
709    def __eq__(self, other: object) -> bool:
710        if not self._ptr:
711            return False
712
713        if isinstance(other, Vec):
714            return self._ptr == other._ptr
715
716        elif isinstance(other, list):
717            return self._ptr == other
718
719        elif isinstance(other, collections.Iterable):
720            # We have to copy here.
721            return self._ptr == list(other)
722
723        return NotImplemented
724
725    def __ne__(self, other: object) -> bool:
726        return not self.__eq__(other)
727
728    def __le__(self, other: list[T]) -> bool:
729        if not self._ptr:
730            return False
731
732        return self._ptr <= other
733
734    def __ge__(self, other: list[T]) -> bool:
735        if not self._ptr:
736            return False
737
738        return self._ptr >= other
739
740    def __lt__(self, other: list[T]) -> bool:
741        if not self._ptr:
742            return False
743
744        return self._ptr < other
745
746    def __gt__(self, other: list[T]) -> bool:
747        if not self._ptr:
748            return False
749
750        return self._ptr > other
751
752    def __bool__(self) -> bool:
753        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

Comparing different collections with Vec have a cost. Depending on what you're comparing it wit.

Any iterable that is not a list or Vec that is used to compare with will get copied into a list, So be careful what you compare a Vec with.

vec = Vec((1,2,3))
# zero-cost
vec == [1, 2, 3]
# Copies {1, 2, 3} -> [1, 2, 3] which can cost.
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 collection will also get pushed into the vec.

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.

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

Return an immutable view over this vector elements.

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().is_none()
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().is_none()
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        iterator = vec.iter()
268
269        # Map each element to a str
270        for element in iterator.map(str):
271            print(element)
272        ```
273        """
274        return _iter.Iter(self)

Return an iterator over this vector elements.

Example
vec = Vec((1,2,3))
iterator = vec.iter()

# Map each element to a str
for element in iterator.map(str):
    print(element)
def is_empty(self) -> bool:
276    def is_empty(self) -> bool:
277        """Returns true if the vector contains no elements."""
278        return not self._ptr

Returns true if the vector contains no elements.

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

Split the vector off at the specified position.

if this vec is empty, self is returned unchanged.

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

print(split, vec)  # [1], [2, 3]
Raises
  • RuntimeError: This method will raise if at > len(self)
def split_first(self) -> Some[tuple[~T, collections.abc.Sequence[~T]]]:
312    def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
313        """Split the first and rest elements of the vector, If empty, `Some[None]` is returned.
314
315        Example
316        -------
317        ```py
318        vec = Vec([1, 2, 3])
319        split = vec.split_first()
320        assert split == Some((1, [2, 3]))
321
322        vec: Vec[int] = Vec()
323        split = vec.split_first()
324        assert split == Some(None)
325        ```
326        """
327        if not self._ptr:
328            return _option.nothing_unchecked()
329
330        first, *rest = self._ptr
331        return _option.Some((first, rest))

Split the first and rest elements of the vector, If empty, Some[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 == Some(None)
def first(self) -> Some[~T]:
333    def first(self) -> _option.Option[T]:
334        """Get the first element in this vec, returning `Some[None]` if there's none.
335
336        Example
337        -------
338        ```py
339        vec = Vec((1,2,3))
340        first = vec.first()
341        assert ~first == 1
342        ```
343        """
344        return self.get(0)

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

Example
vec = Vec((1,2,3))
first = vec.first()
assert ~first == 1
def truncate(self, size: int) -> None:
346    def truncate(self, size: int) -> None:
347        """Shortens the vec, keeping the first `size` elements and dropping the rest.
348
349        Example
350        -------
351        ```py
352        vec = Vec([1,2,3])
353        vec.truncate(1)
354        assert vec == [1]
355        ```
356        """
357        if not self._ptr:
358            return
359
360        self._ptr = 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: collections.abc.Callable[[~T], bool]) -> None:
362    def retain(self, f: collections.Callable[[T], bool]) -> None:
363        """Remove elements from this vec while `f()` returns `True`.
364
365        In other words, filter this vector based on `f()`.
366
367        Example
368        -------
369        ```py
370        vec = Vec([1, 2, 3])
371        vec.retain(lambda elem: elem > 1)
372
373        assert vec == [2, 3]
374        ```
375        """
376        if not self._ptr:
377            return
378
379        self._ptr = [e for e in self._ptr if f(e)]

Remove elements from this vec 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:
381    def swap_remove(self, item: T) -> T:
382        """Remove the first appearance of `item` from this vector and return it.
383
384        Raises
385        ------
386        * `ValueError`: if `item` is not in this vector.
387        * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it.
388
389        Example
390        -------
391        ```py
392        vec = Vector(('a', 'b', 'c'))
393        element = vec.remove('a')
394        assert vec == ['b', 'c'] and element == 'a'
395        ```
396        """
397        if self._ptr is None:
398            raise MemoryError("Vec is unallocated.") from None
399
400        try:
401            i = next(i for i in self._ptr if i == item)
402        except StopIteration:
403            raise ValueError(f"Item `{item}` not in list") from None
404
405        self.remove(i)
406        return i

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 = Vector(('a', 'b', 'c'))
element = vec.remove('a')
assert vec == ['b', 'c'] and element == 'a'
def push(self, item: ~T) -> None:
408    def push(self, item: T) -> None:
409        """Push an element at the end of the vector.
410
411        Example
412        -------
413        ```py
414        vec = Vec()
415        vec.push(1)
416
417        assert vec == [1]
418        ```
419        """
420        if self._capacity is not None:
421            self.push_within_capacity(item)
422            return
423
424        if self._ptr is None:
425            self._ptr = []
426
427        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]':
429    def push_within_capacity(self, x: T) -> Result[None, T]:
430        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
431
432        Example
433        -------
434        ```py
435        vec: Vec[int] = Vec.with_capacity(3)
436        for i in range(3):
437            match vec.push_within_capacity(i):
438                case Ok(_):
439                    print("All good.")
440                case Err(elem):
441                    print("Reached max cap :< cant push", elem)
442        ```
443
444        Or you can also just call `Vec.push` and it will push within capacity if `Vec.capacity()` is not `None`.
445        ```py
446        vec: Vec[int] = Vec.with_capacity(3)
447
448        vec.extend((1, 2, 3))
449        vec.push(4)
450
451        assert vec.len() == 3
452        ```
453        """
454        if self._ptr is None:
455            self._ptr = []
456
457        if self._capacity is not None and self._capacity <= self.len():
458            return _result.Err(x)
459
460        self._ptr.append(x)
461        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 within capacity if Vec.capacity() is not None.

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:
463    def reserve(self, additional: int) -> None:
464        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
465
466        Example
467        -------
468        ```py
469        vec = Vec.with_capacity(3)
470        is_vip = random.choice((True, False))
471
472        for i in range(4):
473            match vec.push_within_capacity(i):
474                case Ok(_):
475                    print("All good")
476                case Err(person):
477                    # If the person is a VIP, then reserve for one more.
478                    if is_vip:
479                        vec.reserve(1)
480                        continue
481
482                    # is_vip was false.
483                    print("Can't reserve for non-VIP members...", person)
484                    break
485        ```
486        """
487        if self._capacity is not None:
488            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:
408    def push(self, item: T) -> None:
409        """Push an element at the end of the vector.
410
411        Example
412        -------
413        ```py
414        vec = Vec()
415        vec.push(1)
416
417        assert vec == [1]
418        ```
419        """
420        if self._capacity is not None:
421            self.push_within_capacity(item)
422            return
423
424        if self._ptr is None:
425            self._ptr = []
426
427        self._ptr.append(item)

An alias for Vec.push method.

def get(self, index: int) -> Some[~T]:
498    def get(self, index: int) -> _option.Option[T]:
499        """Get the item at the given index, or `Some[None]` if its out of bounds.
500
501        Example
502        -------
503        ```py
504        vec = Vec((1, 2, 3))
505        vec.get(0) == Some(1)
506        vec.get(3) == Some(None)
507        ```
508        """
509        try:
510            return _option.Some(self.__getitem__(index))
511        except IndexError:
512            return _option.nothing_unchecked()

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:
514    def insert(self, index: int, value: T) -> None:
515        """Insert an element at the position `index`.
516
517        Example
518        --------
519        ```py
520        vec = Vec((2, 3))
521        vec.insert(0, 1)
522        assert vec == [1, 2, 3]
523        ```
524        """
525        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) -> Some[~T]:
527    def pop(self, index: int = -1) -> _option.Option[T]:
528        """Removes the last element from a vector and returns it, or `sain.Some(None)` if it is empty.
529
530        Example
531        -------
532        ```py
533        vec = Vec((1, 2, 3))
534        assert vec.pop() == Some(3)
535        assert vec == [1, 2]
536        ```
537        """
538        if not self._ptr:
539            return _option.nothing_unchecked()
540
541        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 remove(self, item: ~T) -> None:
543    def remove(self, item: T) -> None:
544        """Remove `item` from this vector.
545
546        Example
547        -------
548        ```py
549        vec = Vector(('a', 'b', 'c'))
550        vec.remove('a')
551        assert vec == ['b', 'c']
552        ```
553        """
554        if not self._ptr:
555            return
556
557        self._ptr.remove(item)

Remove item from this vector.

Example
vec = Vector(('a', 'b', 'c'))
vec.remove('a')
assert vec == ['b', 'c']
def extend(self, iterable: collections.abc.Iterable[~T]) -> None:
559    def extend(self, iterable: collections.Iterable[T]) -> None:
560        """Extend this vector from another iterable.
561
562        Example
563        -------
564        ```py
565        vec = Vec((1, 2, 3))
566        vec.extend((4, 5, 6))
567
568        assert vec == [1, 2, 3, 4, 5, 6]
569        ```
570        """
571        if self._ptr is None:
572            self._ptr = []
573
574        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]:
576    def copy(self) -> Vec[T]:
577        """Create a vector that copies all of its elements and place it into the new one.
578
579        If the vector hasn't been allocated, `self` is returned.
580
581        Example
582        -------
583        ```py
584        original = Vec((1,2,3))
585        copy = original.copy()
586        copy.push(4)
587
588        print(original) # [1, 2, 3]
589        ```
590        """
591        return Vec(self._ptr[:]) if self._ptr is not None else self

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

If the vector hasn't been allocated, self is returned.

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

print(original) # [1, 2, 3]
def clear(self) -> None:
593    def clear(self) -> None:
594        """Clear all elements of this vector.
595
596        Example
597        -------
598        ```py
599        vec = Vec((1,2,3))
600        vec.clear()
601        assert vec.len() == 0
602        ```
603        """
604        if not self._ptr:
605            return
606
607        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:
609    def sort(
610        self,
611        *,
612        key: collections.Callable[[T], SupportsRichComparison] | None = None,
613        reverse: bool = False,
614    ) -> None:
615        """This method sorts the list in place, using only < comparisons between items.
616
617        Example
618        -------
619        ```py
620        vec = Vec((2, 1, 3))
621        vec.sort()
622        assert vec == [1, 2, 3]
623        ```
624        """
625        if not self._ptr:
626            return
627
628        # key can be `None` here just fine, idk why pyright is complaining.
629        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:
631    def index(
632        self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize
633    ) -> int:
634        # << Official documentation >>
635        """Return zero-based index in the vec of the first item whose value is equal to `item`.
636        Raises a ValueError if there is no such item.
637
638        Example
639        -------
640        ```py
641        vec = Vec((1, 2, 3))
642        assert vec.index(2) == 1
643        ```
644        """
645        if self._ptr is None:
646            raise ValueError from None
647
648        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:
650    def count(self, item: T) -> int:
651        """Return the number of occurrences of `item` in the vec.
652
653        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
654
655        Example
656        --------
657        ```py
658        vec = Vec((1, 2, 3, 3))
659        assert vec.count(3) == 2
660        ```
661        """
662        if self._ptr is None:
663            return 0
664
665        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(typing.Protocol):
 79@typing.runtime_checkable
 80class Error(typing.Protocol):
 81    """`Error` is an interface usually used for values that returns `sain.Result[T, E]`
 82
 83    where `E` is an implementation of this interface.
 84
 85    Example
 86    -------
 87    ```py
 88    import requests
 89    from dataclasses import dataclass
 90
 91    from sain import Error, Option, Some
 92    from sain import Result, Ok, Err
 93
 94    # Base error.
 95    class HTTPError(Error): ...
 96
 97    @dataclass
 98    class NotFound(HTTPError):
 99        message = "The response returned [404]: not found."
100        http_status = 404
101        response: requests.Response
102
103        def description(self) -> str:
104            return "Couldn't find what you're looking for " + self.response.url
105
106        # It is not necessary to define this method,
107        # it just gives more context to the user handling this error.
108        def source(self) -> Option[type[HTTPError]]:
109            return Some(HTTPError)
110
111    @dataclass
112    class UserNotFound(NotFound):
113        user_id: int
114
115        def __post_init__(self) -> None:
116            request = self.response.request
117            self.message = f"User {self.user_id} fetched from {request.path_url} was not found."
118
119        # It is not necessary to define this method,
120        # it just gives more context to the user handling this error.
121        def source(self) -> Option[type[NotFound]]:
122            return Some(NotFound)
123
124        def description(self) -> str:
125            return f"Couldn't find the resource: {self.response.raw}."
126
127    # A simple request that handles [404] responses.
128    def request(
129        url: str,
130        resourceful: bool = False,
131        uid: int
132    ) -> Result[requests.Response, HTTPError]:
133        response = requests.get(
134            url,
135            json={"resourceful": True, "user_id": uid}
136            if resourceful else None
137        )
138
139        if response.status_code == 404:
140            if resourceful:
141                return Err(UserNotFound(response, uid))
142            return Err(NotFound(response))
143
144        return Ok(response)
145
146    # Execute the request
147    match request("some-url.com', True, uid=0):
148        case Ok(response):
149            # Deal with the response
150            ...
151        case Err(why):
152            # Deal with the error.
153            print(why.message)
154    ```
155    """
156
157    __slots__ = ("message",)
158
159    def __init__(self, message: str = "") -> None:
160        self.message = message
161        """A basic error message."""
162
163    def source(self) -> Option[type[Error]]:
164        """The source of this error, if any."""
165        return _option.nothing_unchecked()
166
167    def description(self) -> str:
168        """Context for this error."""
169        return ""
170
171    def __repr__(self) -> str:
172        source = None if (src := self.source()).is_none() else src
173        return (
174            f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})'
175        )
176
177    def __str__(self) -> str:
178        return self.message
179
180    # An error is always falsy.
181    def __bool__(self) -> typing.Literal[False]:
182        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(message: str = '')
159    def __init__(self, message: str = "") -> None:
160        self.message = message
161        """A basic error message."""
message

A basic error message.

def source(self) -> Some[type[Error]]:
163    def source(self) -> Option[type[Error]]:
164        """The source of this error, if any."""
165        return _option.nothing_unchecked()

The source of this error, if any.

def description(self) -> str:
167    def description(self) -> str:
168        """Context for this error."""
169        return ""

Context for this error.

@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_unchecked()
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: collections.abc.Callable[[+T], typing.Any]) -> 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) -> Some[+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_unchecked()
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.final
class MaybeUninit(typing.Generic[~T]):
 47@typing.final
 48class MaybeUninit(typing.Generic[T]):
 49    """A wrapper type to construct uninitialized instances of `T`.
 50
 51    This is kinda equivalent to Rust's `MaybeUninit<T>` wrapper.
 52
 53    The difference is what's happening under the hood is when initializing an instance of this object,
 54    nothing really is being initialized, until you call `.write()` to initialize the inner value.
 55
 56    What??
 57    -----
 58    Ok, so `MaybeUninit<T>` is in a sense the unsafe equivalent of `Option<T>`, it either contains a value of type `T`,
 59    or it contains uninitialized memory (`the attribute doesn't exist`).
 60
 61    However, `MaybeUninit` is unable to tell you whether the value it contains is `Some(T)` or `None`, So you
 62    as a programmer is responsible for initializing it.
 63
 64    And by default, `Option<T>` always contain a default value, which is `None`.
 65
 66    Examples
 67    --------
 68    ```py
 69    # Create a list of 3 uninitialized strings preallocated.
 70    pool: list[MaybeUninit[str]] = MaybeUninit.uninit_array(3)
 71    chars = ['a', 'b', 'c']
 72
 73    for index, uninit in enumerate(pool):
 74        uninit.write(chars[index])
 75
 76    assert all(obj.assume_init() for obj in pool)
 77    ```
 78    """
 79
 80    __slots__ = ("__value",)
 81
 82    @typing.overload
 83    def __init__(self) -> None: ...
 84    @typing.overload
 85    def __init__(self, value: T) -> None: ...
 86
 87    def __init__(self, value: T | None = None) -> None:
 88        if value is None:
 89            return None
 90        else:
 91            # we simply pre-initialize the value if it was passed
 92            # when constructing the instance.
 93            self.__value = value
 94
 95    @classmethod
 96    def uninit(cls) -> Self:
 97        """Creates a new `MaybeUninit<T>` in an uninitialized state.
 98
 99        Example
100        -------
101        ```py
102        v: MaybeUninit[str] = MaybeUninit().uninit() # or just MaybeUninit()
103        ```
104        """
105        return cls()
106
107    @classmethod
108    def uninit_array(cls, n: int) -> collections.Sequence[Self]:
109        """Creates an immutable sequence of `MaybeUninit<T>` in an uninitialized state.
110
111        Note that `inner` value doesn't exist in this state until you call `.write` to set it.
112
113        Example
114        -------
115        ```py
116        v = MaybeUninit[str].uninit_array(3)
117        assert len(v) == 3
118        for uninit in v:
119            uninit.write('content')
120
121            initialized = uninit.assume_init()
122        ```
123        """
124        return tuple(cls() for _ in range(n))
125
126    @macros.unsafe
127    def assume_init(self) -> T:
128        """Get the inner value, assuming that it was initialized by the caller.
129
130        It is unsafe and undefined behavior to call this method on an uninitialized state,
131
132        Example
133        -------
134        ```py
135        uninit: MaybeUninit[int] = MaybeUninit.uninit()
136        val = uninit.assume_init() # This is UNSAFE ⚠️
137
138        # Initialize it first.
139        uninit.write(0)
140        val = uninit.assume_init() # This is safe to access.
141        ```
142
143        Raises
144        ------
145        `LookupError`
146            If the `self` is not initialized.
147        """
148        # SAFETY: the caller must guarantee that `self` is initialized.
149        try:
150            return self.__value
151        except AttributeError:
152            raise LookupError("Invalid access to uninitialized value.") from None
153
154    def write(self, value: T) -> T:
155        """Sets the value of the `MaybeUninit[T]`.
156
157        This will overwrite any previous values, if was initialized obviously.
158
159        Example
160        -------
161        ```py
162        def scoped(value: MaybeUninit[bytes]) -> MaybeUninit[bytes]:
163            response = requests.get("...")
164            if response.ok:
165                # If ok, initialize.
166                value.write(response.content)
167
168            return value
169
170        buffer: MaybeUninit[bytes] = MaybeUninit()
171        data = scoped(buffer) # buffer is initialized now.
172        print(data.assume_init())
173        ```
174        """
175        self.__write_mangling(value)
176        return self.assume_init()
177
178    # These are magic methods to bypass the name mangling.
179    def __write_mangling(self, value: T) -> T:
180        # A little hack to bypass name dangling.
181        object.__setattr__(self, "_MaybeUninit__value", value)
182        return value
183
184    def __read_mangling(self) -> T:
185        return getattr(self, "_MaybeUninit__value")
186
187    def __repr__(self) -> str:
188        # We could technically do an `if self: ...` here but
189        # thats not how bool checks work. for example if an empty str
190        # in the content of this cell it will return `False`
191        # which isn't correct. So we actually need to check
192        # whether the attribute is set or not. Also since 3.11,
193        # exception are zero cost, So this shouldn't be a critically inefficient.
194        try:
195            return f"MaybeUninit(value: {self.__read_mangling()!r})"
196        except AttributeError:
197            return "<uninit>"
198
199    __str__ = __repr__
200
201    def __bool__(self) -> bool:
202        """Perform a boolean check on whether the value is initialized or not.
203
204        Example
205        -------
206        ```py
207        v = MaybeUninit[bool]()
208        assert not v
209        ```
210        """
211        return hasattr(self, "_MaybeUninit__value")
212
213    def __eq__(self, value: object, /) -> bool:
214        if not isinstance(value, MaybeUninit):
215            return NotImplemented
216
217        if not self or not value:
218            # either ones not initialized.
219            return False
220
221        return self.__read_mangling() == typing.cast(
222            "MaybeUninit[T]", value.__read_mangling()
223        )
224
225    def __ne__(self, value: object, /) -> bool:
226        return not self.__eq__(value)
227
228    def __hash__(self) -> int:
229        return self.__read_mangling().__hash__()

A wrapper type to construct uninitialized instances of T.

This is kinda equivalent to Rust's MaybeUninit<T> wrapper.

The difference is what's happening under the hood is when initializing an instance of this object, nothing really is being initialized, until you call .write() to initialize the inner value.

What??

Ok, so MaybeUninit<T> is in a sense the unsafe equivalent of Option<T>, it either contains a value of type T, or it contains uninitialized memory (the attribute doesn't exist).

However, MaybeUninit is unable to tell you whether the value it contains is Some(T) or None, So you as a programmer is responsible for initializing it.

And by default, Option<T> always contain a default value, which is None.

Examples
# Create a list of 3 uninitialized strings preallocated.
pool: list[MaybeUninit[str]] = MaybeUninit.uninit_array(3)
chars = ['a', 'b', 'c']

for index, uninit in enumerate(pool):
    uninit.write(chars[index])

assert all(obj.assume_init() for obj in pool)
MaybeUninit(value: Optional[~T] = None)
87    def __init__(self, value: T | None = None) -> None:
88        if value is None:
89            return None
90        else:
91            # we simply pre-initialize the value if it was passed
92            # when constructing the instance.
93            self.__value = value
@classmethod
def uninit(cls) -> Self:
 95    @classmethod
 96    def uninit(cls) -> Self:
 97        """Creates a new `MaybeUninit<T>` in an uninitialized state.
 98
 99        Example
100        -------
101        ```py
102        v: MaybeUninit[str] = MaybeUninit().uninit() # or just MaybeUninit()
103        ```
104        """
105        return cls()

Creates a new MaybeUninit<T> in an uninitialized state.

Example
v: MaybeUninit[str] = MaybeUninit().uninit() # or just MaybeUninit()
@classmethod
def uninit_array(cls, n: int) -> collections.abc.Sequence[typing.Self]:
107    @classmethod
108    def uninit_array(cls, n: int) -> collections.Sequence[Self]:
109        """Creates an immutable sequence of `MaybeUninit<T>` in an uninitialized state.
110
111        Note that `inner` value doesn't exist in this state until you call `.write` to set it.
112
113        Example
114        -------
115        ```py
116        v = MaybeUninit[str].uninit_array(3)
117        assert len(v) == 3
118        for uninit in v:
119            uninit.write('content')
120
121            initialized = uninit.assume_init()
122        ```
123        """
124        return tuple(cls() for _ in range(n))

Creates an immutable sequence of MaybeUninit<T> in an uninitialized state.

Note that inner value doesn't exist in this state until you call .write to set it.

Example
v = MaybeUninit[str].uninit_array(3)
assert len(v) == 3
for uninit in v:
    uninit.write('content')

    initialized = uninit.assume_init()
@macros.unsafe
def assume_init(self) -> ~T:
126    @macros.unsafe
127    def assume_init(self) -> T:
128        """Get the inner value, assuming that it was initialized by the caller.
129
130        It is unsafe and undefined behavior to call this method on an uninitialized state,
131
132        Example
133        -------
134        ```py
135        uninit: MaybeUninit[int] = MaybeUninit.uninit()
136        val = uninit.assume_init() # This is UNSAFE ⚠️
137
138        # Initialize it first.
139        uninit.write(0)
140        val = uninit.assume_init() # This is safe to access.
141        ```
142
143        Raises
144        ------
145        `LookupError`
146            If the `self` is not initialized.
147        """
148        # SAFETY: the caller must guarantee that `self` is initialized.
149        try:
150            return self.__value
151        except AttributeError:
152            raise LookupError("Invalid access to uninitialized value.") from None

Get the inner value, assuming that it was initialized by the caller.

It is unsafe and undefined behavior to call this method on an uninitialized state,

Example
uninit: MaybeUninit[int] = MaybeUninit.uninit()
val = uninit.assume_init() # This is UNSAFE ⚠️

# Initialize it first.
uninit.write(0)
val = uninit.assume_init() # This is safe to access.
Raises
  • LookupError: If the self is not initialized.
  • # Safety ⚠️
  • **Calling this method on None is considered undefined behavior:
def write(self, value: ~T) -> ~T:
154    def write(self, value: T) -> T:
155        """Sets the value of the `MaybeUninit[T]`.
156
157        This will overwrite any previous values, if was initialized obviously.
158
159        Example
160        -------
161        ```py
162        def scoped(value: MaybeUninit[bytes]) -> MaybeUninit[bytes]:
163            response = requests.get("...")
164            if response.ok:
165                # If ok, initialize.
166                value.write(response.content)
167
168            return value
169
170        buffer: MaybeUninit[bytes] = MaybeUninit()
171        data = scoped(buffer) # buffer is initialized now.
172        print(data.assume_init())
173        ```
174        """
175        self.__write_mangling(value)
176        return self.assume_init()

Sets the value of the MaybeUninit[T].

This will overwrite any previous values, if was initialized obviously.

Example
def scoped(value: MaybeUninit[bytes]) -> MaybeUninit[bytes]:
    response = requests.get("...")
    if response.ok:
        # If ok, initialize.
        value.write(response.content)

    return value

buffer: MaybeUninit[bytes] = MaybeUninit()
data = scoped(buffer) # buffer is initialized now.
print(data.assume_init())
@typing.runtime_checkable
class From(typing.Protocol[-T_co]):
112@typing.runtime_checkable
113class From(typing.Protocol[T_co]):
114    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
115
116    As the Rust documentation says, One should always prefer implementing From over Into
117    because implementing From automatically provides one with an implementation of Into.
118
119    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
120    that impl `From<T>`.
121
122    So for the sake of simplicity, You should implement whichever interface you want deal with,
123    Or simply, implement both as the same time.
124
125    Example
126    -------
127    ```py
128    @dataclass
129    class Id(From[str]):
130        value: int
131
132        @classmethod
133        def from_t(cls, value: str) -> Self:
134            return cls(value=int(value))
135
136    ```
137    """
138
139    __slots__ = ()
140
141    @classmethod
142    def from_t(cls, value: T_co) -> Self:
143        """Perform the conversion."""
144        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)
1739def _no_init_or_replace_init(self, *args, **kwargs):
1740    cls = type(self)
1741
1742    if cls._is_protocol:
1743        raise TypeError('Protocols cannot be instantiated')
1744
1745    # Already using a custom `__init__`. No need to calculate correct
1746    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1747    if cls.__init__ is not _no_init_or_replace_init:
1748        return
1749
1750    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1751    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1752    # searches for a proper new `__init__` in the MRO. The new `__init__`
1753    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1754    # instantiation of the protocol subclass will thus use the new
1755    # `__init__` and no longer call `_no_init_or_replace_init`.
1756    for base in cls.__mro__:
1757        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1758        if init is not _no_init_or_replace_init:
1759            cls.__init__ = init
1760            break
1761    else:
1762        # should not happen
1763        cls.__init__ = object.__init__
1764
1765    cls.__init__(self, *args, **kwargs)
@classmethod
def from_t(cls, value: -T_co) -> Self:
141    @classmethod
142    def from_t(cls, value: T_co) -> Self:
143        """Perform the conversion."""
144        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class TryFrom(typing.Protocol[-T_co, ~E]):
147@typing.runtime_checkable
148class TryFrom(typing.Protocol[T_co, E]):
149    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
150    It is the reciprocal of `TryInto`.
151
152    It is useful to implement this when you know that the conversion may fail in some way.
153
154    Generic Implementations
155    -------------------
156    This interface takes two type arguments, and return `Result<Self, E>`
157
158    * `T`: Which's the first generic `T` is the type that's being converted from.
159    * `E`: If the conversion fails in a way, this is what will return as the error.
160    * `Self`: Which's the instance of the class that is being converted into.
161
162    Example
163    -------
164    ```py
165    @dataclass
166    class Id(TryFrom[str, str]):
167        value: int
168
169        @classmethod
170        def try_from(cls, value: str) -> Result[Self, str]:
171            if not value.isnumeric():
172                # NaN
173                return Err(f"Couldn't convert: {value} to self")
174            # otherwise convert it to an Id instance.
175            return Ok(value=cls(int(value)))
176    ```
177    """
178
179    __slots__ = ()
180
181    @classmethod
182    def try_from(cls, value: T_co) -> Result[Self, E]:
183        """Perform the conversion."""
184        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)
1739def _no_init_or_replace_init(self, *args, **kwargs):
1740    cls = type(self)
1741
1742    if cls._is_protocol:
1743        raise TypeError('Protocols cannot be instantiated')
1744
1745    # Already using a custom `__init__`. No need to calculate correct
1746    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1747    if cls.__init__ is not _no_init_or_replace_init:
1748        return
1749
1750    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1751    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1752    # searches for a proper new `__init__` in the MRO. The new `__init__`
1753    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1754    # instantiation of the protocol subclass will thus use the new
1755    # `__init__` and no longer call `_no_init_or_replace_init`.
1756    for base in cls.__mro__:
1757        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1758        if init is not _no_init_or_replace_init:
1759            cls.__init__ = init
1760            break
1761    else:
1762        # should not happen
1763        cls.__init__ = object.__init__
1764
1765    cls.__init__(self, *args, **kwargs)
@classmethod
def try_from(cls, value: -T_co) -> Union[Ok[Self], Err[~E]]:
181    @classmethod
182    def try_from(cls, value: T_co) -> Result[Self, E]:
183        """Perform the conversion."""
184        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class Into(typing.Protocol[+T_cov]):
187@typing.runtime_checkable
188class Into(typing.Protocol[T_cov]):
189    """Conversion from `self`, which may or may not be expensive.
190
191    Example
192    -------
193    ```py
194    @dataclass
195    class Id(Into[str]):
196        value: int
197
198        def into(self) -> str:
199            return str(self.value)
200    ```
201    """
202
203    __slots__ = ()
204
205    def into(self) -> T_cov:
206        """Perform the conversion."""
207        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)
1739def _no_init_or_replace_init(self, *args, **kwargs):
1740    cls = type(self)
1741
1742    if cls._is_protocol:
1743        raise TypeError('Protocols cannot be instantiated')
1744
1745    # Already using a custom `__init__`. No need to calculate correct
1746    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1747    if cls.__init__ is not _no_init_or_replace_init:
1748        return
1749
1750    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1751    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1752    # searches for a proper new `__init__` in the MRO. The new `__init__`
1753    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1754    # instantiation of the protocol subclass will thus use the new
1755    # `__init__` and no longer call `_no_init_or_replace_init`.
1756    for base in cls.__mro__:
1757        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1758        if init is not _no_init_or_replace_init:
1759            cls.__init__ = init
1760            break
1761    else:
1762        # should not happen
1763        cls.__init__ = object.__init__
1764
1765    cls.__init__(self, *args, **kwargs)
def into(self) -> +T_cov:
205    def into(self) -> T_cov:
206        """Perform the conversion."""
207        raise NotImplementedError

Perform the conversion.

@typing.runtime_checkable
class TryInto(typing.Protocol[~T, ~E]):
210@typing.runtime_checkable
211class TryInto(typing.Protocol[T, E]):
212    """An attempted conversion from `self`, which may or may not be expensive.
213
214    It is useful to implement this when you know that the conversion may fail in some way.
215
216    Generic Implementations
217    -------------------
218    This interface takes two type arguments, and return `Result<T, E>`
219
220    * `T`: The first generic `T` is the type that's being converted into.
221    * `E`: If the conversion fails in a way, this is what will return as the error.
222
223    Example
224    -------
225    ```py
226    @dataclass
227    class Id(TryInto[int, str]):
228        value: str
229
230        def try_into(self) -> Result[int, str]:
231            if not self.value.isnumeric():
232                return Err(f"{self.value} is not a number...")
233            return Ok(int(self.value))
234    ```
235    """
236
237    __slots__ = ()
238
239    def try_into(self) -> Result[T, E]:
240        """Perform the conversion."""
241        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)
1739def _no_init_or_replace_init(self, *args, **kwargs):
1740    cls = type(self)
1741
1742    if cls._is_protocol:
1743        raise TypeError('Protocols cannot be instantiated')
1744
1745    # Already using a custom `__init__`. No need to calculate correct
1746    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1747    if cls.__init__ is not _no_init_or_replace_init:
1748        return
1749
1750    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1751    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1752    # searches for a proper new `__init__` in the MRO. The new `__init__`
1753    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1754    # instantiation of the protocol subclass will thus use the new
1755    # `__init__` and no longer call `_no_init_or_replace_init`.
1756    for base in cls.__mro__:
1757        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1758        if init is not _no_init_or_replace_init:
1759            cls.__init__ = init
1760            break
1761    else:
1762        # should not happen
1763        cls.__init__ = object.__init__
1764
1765    cls.__init__(self, *args, **kwargs)
def try_into(self) -> Union[Ok[~T], Err[~E]]:
239    def try_into(self) -> Result[T, E]:
240        """Perform the conversion."""
241        raise NotImplementedError

Perform the conversion.