sain

sain — Write safe Python code like Rust 🦀

sain is a dependency-free Python library which brings the Rust ecosystem to Python, Allowing its users to write safe, idiomatic Python code just like Rust.

What sain provides

If you already know what you are looking for, the fastest way is to use the search bar,

otherwise, you may want to jump into one of these useful sections:

  • Fundamental types, such as slice.
  • collections Implementations of the most common general purpose data structures from Rust's std::collections and friends such as HashMap, Vec.
  • Core error-handling types such as Option and Some variant, Result and Ok, Err variants.
  • The Iterator trait and its adapters.
  • Common traits such as From, Into.
  • Synchronization primitives, includes LazyLock and Once.
  • Support macros such as #[deprecated], todo!(), unimplemented!() and more.

A Tour of sain

The next section will include the most general purpose and notable features of Rust implemented in sain.

Containers and Collections

The option and result modules define optional and error-handling types, Option and Result. The iter module defines the Iterator trait.

For std::collections types, sain exposes Vec to the top level, which can be imported directly, as for the rest of the types, they all exist under sain.collections, notable ones are:

  • Vec - Built on-top of a list object, contains all of Rust's Vec methods.
  • HashMap - Built on-top of a dict object, contains all of Rust's HashMap methods.
  • Bytes and BytesMut - although these are not part of the standard library, it implements bytes::Bytes crate.
  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"""
 32.. include:: ../DOCS.md
 33"""
 34
 35from __future__ import annotations
 36
 37__all__ = (
 38    # async_iter.py
 39    "async_iter",
 40    # cfg.py
 41    "cfg",
 42    "cfg_attr",
 43    # default.py
 44    "default",
 45    "Default",
 46    # option.py
 47    "option",
 48    "Some",
 49    "Option",
 50    "NOTHING",
 51    # iter.py
 52    "Iter",
 53    "Iterator",
 54    "iter",
 55    # macros.py
 56    "macros",
 57    "todo",
 58    "deprecated",
 59    "unimplemented",
 60    "doc",
 61    "include_str",
 62    "include_bytes",
 63    "assert_eq",
 64    "assert_ne",
 65    # futures.py
 66    "futures",
 67    # result.py
 68    "result",
 69    "Ok",
 70    "Err",
 71    "Result",
 72    # collections
 73    "collections",
 74    "Vec",
 75    # error.py
 76    "error",
 77    "Error",
 78    # boxed.py
 79    "boxed",
 80    "Box",
 81    # sync
 82    "sync",
 83    # maybe_uninit.py
 84    "maybe_uninit",
 85    # convert
 86    "convert",
 87    "From",
 88    "TryFrom",
 89    "Into",
 90    "TryInto",
 91    "ToString",
 92    # time
 93    "time",
 94    # misc
 95    "__version__",
 96    "__url__",
 97    "__author__",
 98    "__about__",
 99    "__license__",
100)
101
102from . import async_iter
103from . import boxed
104from . import collections
105from . import convert
106from . import default
107from . import error
108from . import futures
109from . import iter
110from . import macros
111from . import maybe_uninit
112from . import option
113from . import result
114from . import sync
115from . import time
116from ._misc import __about__
117from ._misc import __author__
118from ._misc import __license__
119from ._misc import __url__
120from ._misc import __version__
121from .boxed import Box
122from .cfg import cfg
123from .cfg import cfg_attr
124from .collections import Vec
125from .convert import From
126from .convert import Into
127from .convert import ToString
128from .convert import TryFrom
129from .convert import TryInto
130from .default import Default
131from .error import Error
132from .iter import Iter
133from .iter import Iterator
134from .macros import assert_eq
135from .macros import assert_ne
136from .macros import deprecated
137from .macros import doc
138from .macros import include_bytes
139from .macros import include_str
140from .macros import todo
141from .macros import unimplemented
142from .option import NOTHING
143from .option import Option
144from .option import Some
145from .result import Err
146from .result import Ok
147from .result import Result
@rustc_diagnostic_item('cfg')
def cfg( target_os: Optional[Literal['linux', 'win32', 'darwin', 'macos', 'unix', 'windows', 'ios']] = None, python_version: tuple[int, ...] | None = None, target_arch: Optional[Literal['x86', 'x86_64', 'arm', 'arm64']] = None, impl: Optional[Literal['CPython', 'PyPy', 'IronPython', 'Jython']] = None) -> bool:
174@rustc_diagnostic_item("cfg")
175def cfg(
176    target_os: System | None = None,
177    python_version: tuple[int, ...] | None = None,
178    target_arch: Arch | None = None,
179    impl: Python | None = None,
180) -> bool:
181    """A function that will run the code only if all predicate attributes returns `True`.
182
183    The difference between this function and `cfg_attr` is that this function will not raise an exception.
184    Instead it will return `False` if any of the attributes fails.
185
186    Example
187    -------
188    ```py
189    import sain
190
191    if cfg(target_os="windows"):
192        print("Windows")
193    elif cfg(target_os="linux", target_arch="arm64"):
194        print("Linux")
195    else:
196        print("Something else")
197    ```
198
199    Parameters
200    ----------
201    target_os : `str | None`
202        The targeted operating system that's required for the object to be executed.
203    python_version : `tuple[int, ...] | None`
204        The targeted Python version that's required for the object to be executed. Format must be `(3, ..., ...)`
205    target_arch : `str | None`
206        The CPU targeted architecture that's required for the object to be executed.
207    impl : `str | None`
208        The Python implementation that's required for the object to be executed.
209
210    Returns
211    -------
212    `bool`
213        The condition that was checked.
214    """
215    checker = _AttrCheck(
216        lambda: None,
217        no_raise=True,
218        target_os=target_os,
219        python_version=python_version,
220        target_arch=target_arch,
221        impl=impl,
222    )
223    return checker.check_once()

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 that's required for the object to be executed.
  • python_version (tuple[int, ...] | None): The targeted Python version that's required for the object to be executed. Format must be (3, ..., ...)
  • target_arch (str | None): The CPU targeted architecture that's required for the object to be executed.
  • impl (str | None): The Python implementation that's required for the object to be executed.
Returns
  • bool: The condition that was checked.
Implementations

This function implements cfg in Rust.

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

Conditional runtime object configuration based on passed arguments.

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

Example
import sain

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

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

# An instance will not be created if raised.
zoo = Zoo()
# RuntimeError("class Zoo requires PyPy implementation")
Parameters
  • target_os (str | None): The targeted operating system that's required for the object.
  • python_version (tuple[int, int, int] | None): The targeted Python version that's required for the object. Format must be (3, ..., ...).
  • target_arch (str | None): The CPU targeted architecture that's required for the object.
  • impl (str | None): The Python implementation that's required for the object.
Raises
  • RuntimeError: This fails if any of the attributes returns False.
  • ValueError: If the passed Python implementation is unknown.
Implementations

This function implements cfg_attr in Rust.

@rustc_diagnostic_item('Default')
@typing.runtime_checkable
class Default(typing.Protocol[+_T_co]):
59@rustc_diagnostic_item("Default")
60@typing.runtime_checkable
61class Default(typing.Protocol[_T_co]):
62    """An interface for an object that has a default value.
63
64    Example
65    -------
66    ```py
67    from sain import Default
68
69    class Cache(Default[dict[str, Any]]):
70
71        @staticmethod
72        def default() -> dict[str, Any]:
73            return {}
74
75    cache = Cache.default()
76    print(cache)
77    assert isinstance(cache, Default)
78    # {}
79    ```
80    """
81
82    __slots__ = ()
83
84    # FIXME: `impl Default for String` knows the type of `Self` is `String` but we can't do that.
85    # So generics is the only way to achieve the same effect. But `Default` in Rust is not generic.
86    # in the future we just swap to `Self`.
87    @staticmethod
88    @rustc_diagnostic_item("default_fn")
89    def default() -> _T_co:
90        """Return the default value of the object."""
91        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)
# {}
Implementations

This class implements Default in Rust.

Default(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
@staticmethod
@rustc_diagnostic_item('default_fn')
def default() -> +_T_co:
87    @staticmethod
88    @rustc_diagnostic_item("default_fn")
89    def default() -> _T_co:
90        """Return the default value of the object."""
91        raise NotImplementedError

Return the default value of the object.

Implementations

This function implements .Default.html#tymethodsain.default">default_fn in Rust.

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

The Option type represents optional value, higher-level abstraction over the None type.

It combines union of T | None in one convenient structure, allowing the users to manipulate and propagate the contained value idiomatically.

An Option value have multiple use cases:

  • Initial values.
  • Return value for functions that may or may not contain a return value.
  • Optional parameters, class fields.
  • Swapping values.
Example
# the actual implementation of the object.
from sain import Some
# Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like.
from sain import Option

def divide(numerator: float, denominator: float) -> Option[float]:
    if denominator == 0.0:
        return Some(None)
    return Some(numerator / denominator)

# Returns Option[float]
result = divide(2.0, 3.0)

# Pattern match to retrieve the value
match result:
    # The division is valid.
    case Some(x):
        print("Result:", x)
    # Invalid division, this is Some(None)
    case _:
        print("cannot divide by 0")

Converting Nones into RuntimeErrors

Sometimes we want to extract the value and cause an error to the caller if it doesn't exist,

because handling Some/None can be tedious, luckily we have several ways to deal with this.

def ipaddr(s: str) -> Option[tuple[int, int, int, int]]:
    match s.split('.'):
        case [a, b, c, d]:
            return Some((int(a), int(b), int(c), int(d)))
        case _:
            return Some(None)

# calls `unwrap()` for you.
ip = ~ipaddr("192.168.1.19")
# causes a `RuntimeError` if it returns `None`.
ip = ipaddr("192.168.1.19").unwrap()
# causes a `RuntimeError` if it returns `None`, with a context message.
ip = ipaddr("192.168.1.19").expect("i need an ip address :<")

The ~ operator will result in tuple[int, int, int, int] if the parsing succeed. unless the contained value is None, it will cause a RuntimeError.

If the value must be present, use unwrap_or, which takes a default parameter and returns it in-case ipaddr returns None

ip = ipaddr("blah blah blah").unwrap_or("192.168.1.255")
# Results: 192.168.1.255

Overall, this type provides many other functional methods such as map, filter.

boolean checks such as is_some, is_none, converting between Option and Result using ok_or, and many more.

Implementations

This class implements Option in Rust.

Some(value: Optional[~T], /)
140    def __init__(self, value: T | None, /) -> None:
141        self._value = value
@staticmethod
def default() -> 'Option[T]':
143    @staticmethod
144    def default() -> Option[T]:
145        """Default value for `Option<T>`. Returns `None` wrapped in `Some`.
146
147        Example
148        -------
149        ```py
150        assert Some[int].default() is NOTHING
151        ```
152        """
153        return NOTHING

Default value for Option<T>. Returns None wrapped in Some.

Example
assert Some[int]sain.default() is NOTHING
def transpose(self) -> Optional[~T]:
157    def transpose(self) -> T | None:
158        """Convert `Option[T]` into `T | None`.
159
160        Examples
161        --------
162        ```py
163        opt = Some('char')
164        x = opt.transpose()
165        assert x == 'char'
166
167        opt = Some(None)
168        assert opt.transpose() is None
169        ```
170        """
171        return self._value

Convert Option[T] into T | None.

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

opt = Some(None)
assert opt.transpose() is None
def unwrap(self) -> ~T:
173    def unwrap(self) -> T:
174        """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`.
175
176        It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns.
177
178        Example
179        -------
180        ```py
181        value = Some(5)
182        print(value.unwrap())
183        # 5
184
185        value = Some(None)
186        print(value.unwrap())
187        # RuntimeError
188        ```
189
190        Raises
191        ------
192        `RuntimeError`
193            If the inner value is `None`.
194        """
195        if self._value is None:
196            raise RuntimeError("Called `Option.unwrap()` on `None`.") from None
197
198        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, and instead use safer options such as unwrap_or or match patterns.

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:
200    def unwrap_or(self, default: T, /) -> T:
201        """Unwrap the inner value either returning if its not `None` or returning `default`.
202
203        Example
204        -------
205        ```py
206        value = Some(5)
207        print(value.unwrap_or(10))
208        # 5
209
210        # Type hint is required here.
211        value: Option[int] = Some(None)
212        print(value.unwrap_or(10))
213        # 10
214        ```
215        """
216        if self._value is None:
217            return default
218
219        return self._value

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

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

# Type hint is required here.
value: Option[int] = Some(None)
print(value.unwrap_or(10))
# 10
def unwrap_or_else(self, f: Callable[[], ~T], /) -> ~T:
221    def unwrap_or_else(self, f: FnOnce[T], /) -> T:
222        """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value.
223
224        Example
225        -------
226        ```py
227        value = Some(5)
228        print(value.unwrap_or_else(lambda: 10))
229        # 5
230
231        value: Option[bool] = Some(None)
232        print(value.unwrap_or_else(lambda: True))
233        # True
234        ```
235        """
236        if self._value is None:
237            return f()
238
239        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:
241    @macros.unsafe
242    def unwrap_unchecked(self) -> T:
243        """Returns the contained Some value without checking that the value is not None.
244
245        Example
246        -------
247        ```py
248        v: Option[float] = Some(1.2)
249        v.unwrap_unchecked() # 1.2
250
251        v: Option[float] = Some(None)
252        print(v.unwrap_unchecked()) # Undefined Behavior
253        ```
254        """
255        #! SAFETY: The caller guarantees that the value is not None.
256        return self._value  # pyright: ignore

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

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

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

Safety ⚠️

Calling this method without knowing its output is considered undefined behavior.

def expect(self, message: str, /) -> ~T:
258    def expect(self, message: str, /) -> T:
259        """Returns the contained `Some` value.
260
261        raises if the value is `None` with a custom provided `message`.
262
263        Example
264        -------
265        ```py
266        value = Some("Hello")
267
268        print(value.expect("Value is None"))
269        # "Hello"
270
271        value: Option[str] = Some(None)
272        print(value.expect("Value is None"))
273        # RuntimeError("Value is None")
274        ```
275        """
276        if self._value is None:
277            raise RuntimeError(message)
278
279        return self._value

Returns the contained Some value.

raises if the value is None with a custom provided message.

Example
value = Some("Hello")

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

value: Option[str] = Some(None)
print(value.expect("Value is None"))
# RuntimeError("Value is None")
def map(self, f: Callable[[~T], ~U], /) -> 'Option[U]':
283    def map(self, f: Fn[T, U], /) -> Option[U]:
284        """Map the inner value to another type. Returning `Some(None)` if `T` is `None`.
285
286        Example
287        -------
288        ```py
289        value = Some(5.0)
290
291        print(value.map(lambda x: x * 2.0))
292        # Some(10.0)
293
294        value: Option[bool] = Some(None)
295        print(value)
296        # Some(None)
297        ```
298        """
299        if self._value is None:
300            return NOTHING
301
302        return Some(f(self._value))

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

Example
value = Some(5.0)

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

value: Option[bool] = Some(None)
print(value)
# Some(None)
def map_or(self, default: ~U, f: Callable[[~T], ~U], /) -> ~U:
304    def map_or(self, default: U, f: Fn[T, U], /) -> U:
305        """Map the inner value to another type or return `default` if its `None`.
306
307        Example
308        -------
309        ```py
310        value: Option[float] = Some(5.0)
311
312        # map to int.
313        print(value.map_or(0, int))
314        # 6
315
316        value: Option[float] = Some(None)
317        print(value.map_or(0, int)
318        # 0
319        ```
320        """
321        if self._value is None:
322            return default
323
324        return f(self._value)

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

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

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

value: Option[float] = Some(None)
print(value.map_or(0, int)
# 0
def map_or_else(self, default: Callable[[], ~U], f: Callable[[~T], ~U], /) -> ~U:
326    def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U:
327        """Map the inner value to another type, or return `default()` if its `None`.
328
329        Example
330        -------
331        ```py
332        def default() -> int:
333            return sys.getsizeof(object())
334
335        value: Option[float] = Some(5.0)
336
337        # map to int.
338        print(value.map_or_else(default, int))
339        # 6
340
341        value: Option[float] = Some(None)
342        print(value.map_or_else(default, int)
343        # 28 <- size of object()
344        ```
345        """
346        if self._value is None:
347            return default()
348
349        return f(self._value)

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

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

value: Option[float] = Some(5.0)

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

value: Option[float] = Some(None)
print(value.map_or_else(default, int)
# 28 <- size of object()
def filter(self, predicate: Callable[[~T], bool]) -> 'Option[T]':
351    def filter(self, predicate: Fn[T, bool]) -> Option[T]:
352        """Returns `Some(None)` if the contained value is `None`,
353
354        otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`.
355
356        Example
357        -------
358        ```py
359        value = Some([1, 2, 3])
360
361        print(value.filter(lambda x: 1 in x))
362        # Some([1, 2, 3])
363
364        value: Option[int] = Some([1, 2, 3]) # or Some(None)
365        print(value.filter(lambda x: 1 not in x))
366        # None
367        ```
368        """
369        if (value := self._value) is not None:
370            if predicate(value):
371                return Some(value)
372
373        return NOTHING

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

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

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

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

value: Option[int] = Some([1, 2, 3]) # or Some(None)
print(value.filter(lambda x: 1 not in x))
# None
def ok_or(self, err: ~U) -> '_result.Result[T, U]':
375    def ok_or(self, err: U) -> _result.Result[T, U]:
376        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`.
377
378        Example
379        -------
380        ```py
381        xyz: Option[str] = Some("foo")
382        assert xyz.ok_or(None) == Ok("foo")
383
384        xyz: Option[str] = Some(None)
385        assert xyz.ok_or(None) == Err(None)
386        ```
387        """
388        if self._value is None:
389            return _result.Err(err)
390
391        return _result.Ok(self._value)

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

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

xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
def ok_or_else(self, err: Callable[[], ~U]) -> '_result.Result[T, U]':
393    def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]:
394        """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`.
395
396        Example
397        -------
398        ```py
399        xyz: Option[str] = Some("foo")
400        assert xyz.ok_or(None) == Ok("foo")
401
402        xyz: Option[str] = Some(None)
403        assert xyz.ok_or(None) == Err(None)
404        ```
405        """
406        if self._value is None:
407            return _result.Err(err())
408
409        return _result.Ok(self._value)

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

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

xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
def zip(self, other: 'Option[U]') -> 'Option[tuple[T, U]]':
411    def zip(self, other: Option[U]) -> Option[tuple[T, U]]:
412        """Zips `self` with `other`.
413
414        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`.
415
416        Example
417        -------
418        ```py
419        x = Some(1)
420        y = Some("hi")
421        z: Option[str] = Some(None)
422
423        assert x.zip(y) == Some((1, "hi"))
424        assert x.zip(z) == Some(None)
425        ```
426        """
427        if self._value is not None and other._value is not None:
428            return Some((self._value, other._value))
429
430        return NOTHING

Zips self with other.

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

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

assert x.zip(y) == Some((1, "hi"))
assert x.zip(z) == Some(None)
def zip_with(self, other: 'Option[U]', f: Callable[[~T, ~U], +T_co]) -> 'Option[T_co]':
432    def zip_with(
433        self, other: Option[U], f: collections.Callable[[T, U], T_co]
434    ) -> Option[T_co]:
435        """Zips `self` with `other` using function `f`.
436
437        if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`.
438
439        Example
440        -------
441        ```py
442        @dataclass
443        class Point:
444            x: float
445            y: float
446
447        x, y = Some(32.1), Some(42.4)
448        assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
449        ```
450        """
451        if self._value is not None and other._value is not None:
452            return Some(f(self._value, other._value))
453
454        return NOTHING

Zips self with other using function f.

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

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

x, y = Some(32.1), Some(42.4)
assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
def take(self) -> 'Option[T]':
458    def take(self) -> Option[T]:
459        """Take the value from `self` Setting it to `None`, and then return `Some(v)`.
460
461        If you don't care about the original value, use `Option.clear()` instead.
462
463        Example
464        -------
465        ```py
466        original = Some("Hi")
467        new = original.take()
468
469        print(original, new)
470        # None, Some("Hi")
471        ```
472        """
473        if self._value is None:
474            return NOTHING
475
476        val = self._value
477        self._value = None
478        return Some(val)

Take the value from self Setting it to None, and then return Some(v).

If you don't care about the original value, use Option.clear() instead.

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

print(original, new)
# None, Some("Hi")
def take_if(self, predicate: Callable[[~T], bool]) -> 'Option[T]':
480    def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]:
481        """Take the value from `Self`, Setting it to `None` only if predicate returns `True`.
482
483        If you don't care about the original value, use `Option.clear_if()` instead.
484
485        Example
486        -------
487        ```py
488        def validate(email: str) -> bool:
489            # you can obviously validate this better.
490            return email.find('@') == 1
491
492        original = Some("flex@gg.com")
493        valid = original.take_if(validate)
494        assert is_allowed.is_some() and original.is_none()
495
496        original = Some("mail.example.com")
497        invalid = original.take_if(validate)
498        assert invalid.is_none() and original.is_some()
499        ```
500        """
501        if self.map_or(False, predicate):
502            return self.take()
503
504        return NOTHING

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

If you don't care about the original value, use Option.clear_if() instead.

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 clear(self) -> None:
506    def clear(self) -> None:
507        """Clear the inner value, setting it to `None`.
508
509        If you care about the original value, use `Option.take()` instead.
510
511        Example
512        -------
513        ```py
514        value = Some("Hello")
515        value.clear()
516        assert value.is_none()
517        ```
518        """
519        self._value = None

Clear the inner value, setting it to None.

If you care about the original value, use Option.take() instead.

Example
value = Some("Hello")
value.clear()
assert value.is_none()
def clear_if(self, predicate: Callable[[~T], bool]) -> None:
521    def clear_if(self, predicate: Fn[T, bool]) -> None:
522        """Clear the inner value, setting it to `None` if the predicate returns `True`.
523
524        If you care about the original value, use `Option.take_if()` instead.
525
526        Example
527        -------
528        ```py
529        value = Some("Hello")
530        value.clear_if(lambda x: x == "Hello")
531        assert value.is_none()
532        ```
533        """
534        if self._value is not None and predicate(self._value):
535            self._value = None

Clear the inner value, setting it to None if the predicate returns True.

If you care about the original value, use Option.take_if() instead.

Example
value = Some("Hello")
value.clear_if(lambda x: x == "Hello")
assert value.is_none()
def replace(self, value: ~T) -> 'Option[T]':
537    def replace(self, value: T) -> Option[T]:
538        """Replace the contained value with another value.
539
540        Use `Option.insert` if you want to return the original value
541        that got inserted instead of `self`
542
543        Example
544        -------
545        ```py
546        value: Option[str] = Some(None)
547        value.replace("Hello")
548        # Some("Hello")
549        ```
550        """
551        self._value = value
552        return self

Replace the contained value with another value.

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

Example
value: Option[str] = Some(None)
value.replace("Hello")
# Some("Hello")
def insert(self, value: ~T) -> ~T:
554    def insert(self, value: T) -> T:
555        """Insert a value into the option, and then return a reference to it.
556
557        This will overwrite the old value if it was already contained.
558
559        Example
560        -------
561        ```py
562        flag: Option[bool] = Some(None)
563        flag_ref = flag.insert(True)
564        assert flag_ref == True
565        assert flag.unwrap() == True
566        ```
567        """
568        self._value = value
569        return value

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

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

Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
def get_or_insert(self, value: ~T) -> ~T:
571    def get_or_insert(self, value: T) -> T:
572        """Insert a value into the option if it was `None`,
573        and then return a reference to it.
574
575        Example
576        -------
577        ```py
578        state: Option[bool] = Some(None)
579        assert state.get_or_insert(True) is True
580        assert state.get_or_insert(False) is True
581        ```
582        """
583        if self._value is not None:
584            return self._value
585
586        self._value = value
587        return value

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

Example
state: Option[bool] = Some(None)
assert state.get_or_insert(True) is True
assert state.get_or_insert(False) is True
def get_or_insert_with(self, f: Callable[[], ~T]) -> ~T:
589    def get_or_insert_with(self, f: FnOnce[T]) -> T:
590        """Insert a value into the option computed from `f()` if it was `None`,
591        and then return a reference to it.
592
593        Example
594        -------
595        ```py
596        flag: Option[bool] = Some(None)
597        flag_ref = flag.insert(True)
598        assert flag_ref == True
599        assert flag.unwrap() == True
600        ```
601        """
602        if self._value is not None:
603            return self._value
604
605        v = self._value = f()
606        return v

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

Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
def and_ok(self, optb: 'Option[T]') -> 'Option[T]':
608    def and_ok(self, optb: Option[T]) -> Option[T]:
609        """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`.
610
611        aliases: `Option::and`
612
613        Example
614        -------
615        ```py
616        x = Some(1)
617        y: Option[str] = Some(None)
618        assert x.and_ok(y) == Some(None)
619
620        x: Option[str] = Some(None)
621        y = Some(1)
622        assert x.and_ok(y) == Some(None)
623
624        x: Option[str] = Some("hi")
625        y = Some(100)
626        assert x.and_ok(y) == Some(100)
627        ```
628        """
629        if self._value is None or optb._value is None:
630            return optb
631
632        return NOTHING

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

aliases: Option::and

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

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

x: Option[str] = Some("hi")
y = Some(100)
assert x.and_ok(y) == Some(100)
def and_then(self, f: 'Fn[T, Option[T]]') -> 'Option[T]':
634    def and_then(self, f: Fn[T, Option[T]]) -> Option[T]:
635        """Returns `Some(None)` if the contained value is `None`, otherwise call `f()`
636        on `T` and return `Option[T]`.
637
638        Example
639        -------
640        ```py
641        value = Some(5)
642        print(value.and_then(lambda x: Some(x * 2)))
643        # Some(10)
644
645        value: Option[int] = Some(None)
646        print(value.and_then(lambda x: Some(x * 2)))
647        # Some(None)
648        ```
649        """
650        if self._value is None:
651            return NOTHING
652
653        return f(self._value)

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

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

value: Option[int] = Some(None)
print(value.and_then(lambda x: Some(x * 2)))
# Some(None)
def inspect(self, f: Callable[[~T], typing.Any]) -> 'Option[T]':
655    def inspect(self, f: Fn[T, typing.Any]) -> Option[T]:
656        """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing.
657
658        Example
659        -------
660        ```py
661        def debug(x: str) -> None:
662            print("Debugging:", x)
663
664        value = Some("foo")
665        inner = value.inspect(debug).expect("no value to debug")
666        # prints: Debugging: "foo"
667
668        value: Option[str] = Some(None)
669        value.inspect(debug) # prints nothing
670        """
671        if self._value is not None:
672            f(self._value)
673
674        return self

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

Example

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

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

prints: Debugging: "foo"

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

def iter(self) -> sain.iter.ExactSizeIterator[~T]:
678    def iter(self) -> _iter.ExactSizeIterator[T]:
679        """Returns an iterator over the contained value.
680
681        Example
682        -------
683        ```py
684        from sain import Some
685        value = Some("gg").iter()
686        assert value.next() == Some("gg")
687
688        value: Option[int] = Some(None)
689        assert value.iter().next().is_none()
690        ```
691        """
692        if self._value is None:
693            return _iter.empty()
694
695        return _iter.once(self._value)

Returns an iterator over the contained value.

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

value: Option[int] = Some(None)
assert value.iter().next().is_none()
def is_some(self) -> bool:
699    def is_some(self) -> bool:
700        """Returns `True` if the contained value is not `None`, otherwise returns `False`.
701
702        Example
703        -------
704        ```py
705        value = Some(5)
706        print(value.is_some())
707        # True
708
709        value: Option[int] = Some(None)
710        print(value.is_some())
711        # False
712        ```
713        """
714        return self._value is not None

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

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

value: Option[int] = Some(None)
print(value.is_some())
# False
def is_some_and(self, predicate: Callable[[~T], bool]) -> bool:
716    def is_some_and(self, predicate: Fn[T, bool]) -> bool:
717        """Returns `True` if the contained value is not `None` and
718        the predicate returns `True`, otherwise returns `False`.
719
720        Example
721        -------
722        ```py
723        value = Some(5)
724        print(value.is_some_and(lambda x: x > 3))
725        # True
726
727        value: Option[int] = Some(None)
728        print(value.is_some_and(lambda x: x > 3))
729        # False
730        ```
731        """
732        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:
734    def is_none(self) -> bool:
735        """Returns `True` if the contained value is `None`, otherwise returns `False`.
736
737        Example
738        -------
739        ```py
740        value = Some(5)
741        print(value.is_none())
742        # False
743
744        value: Option[int] = Some(None)
745        print(value.is_none())
746        # True
747        ```
748        """
749        return self._value is None

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

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

value: Option[int] = Some(None)
print(value.is_none())
# True
def is_none_or(self, f: Callable[[~T], bool]) -> bool:
751    def is_none_or(self, f: Fn[T, bool]) -> bool:
752        """Returns `True` if the contained value is `None` or the predicate returns `True`,
753        otherwise returns `False`.
754
755        Example
756        -------
757        ```py
758        value = Some(5)
759        print(value.is_none_or(lambda x: x > 3))
760        # False
761
762        value: Option[int] = Some(None)
763        print(value.is_none_or(lambda x: x > 3))
764        # True
765        ```
766        """
767        match self._value:
768            case None:
769                return True
770            case x:
771                return f(x)

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

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

value: Option[int] = Some(None)
print(value.is_none_or(lambda x: x > 3))
# True
Option = 'Some[T]'
NOTHING = None
@rustc_diagnostic_item('Iter')
@typing.final
@diagnostic
class Iter(typing.Generic[~Item], sain.Iterator[~Item]):
1041@rustc_diagnostic_item("Iter")
1042@typing.final
1043@diagnostic
1044class Iter(typing.Generic[Item], Iterator[Item]):
1045    """a lazy iterator that has its items ready in-memory.
1046
1047    This is similar to Rust `std::slice::Iter<T>` item which iterables can build
1048    from this via `.iter()` method.
1049
1050    Example
1051    -------
1052    ```py
1053    iterator = Iter([1, 2, 3])
1054
1055    # Limit the results to 2.
1056    for item in iterator.take(2):
1057        print(item)
1058    # 1
1059    # 2
1060
1061    # Filter the results.
1062    for item in iterator.filter(lambda item: item > 1):
1063        print(item)
1064    # 2
1065    # 3
1066    # 3
1067
1068    # Indexing is supported.
1069    print(iterator[0])
1070    # 1
1071    ```
1072
1073    Parameters
1074    ----------
1075    items: `Iterable[Item]`
1076        The items to iterate over. This can be anything that implements `__iter__` and `__next__`.
1077    """
1078
1079    __slots__ = ("_it",)
1080
1081    def __init__(self, iterable: collections.Iterable[Item]) -> None:
1082        self._it = iter(iterable)
1083
1084    def clone(self) -> Iter[Item]:
1085        """Return a copy of this iterator.
1086
1087        ```py
1088        it = Iter([1, 2, 3])
1089
1090        for i in it.clone():
1091            ...
1092
1093        # The actual iterator hasn't been exhausted.
1094        assert it.count() == 3
1095        ```
1096        """
1097        return Iter(copy.copy(self._it))
1098
1099    def __next__(self) -> Item:
1100        return next(self._it)
1101
1102    def __getitem__(self, index: int) -> Item:
1103        return self.skip(index).first().unwrap_or_else(oob)
1104
1105    def __contains__(self, item: Item) -> bool:
1106        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 .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__.
Implementations

This class implements Iter in Rust.

Iter(iterable: Iterable[~Item])
1081    def __init__(self, iterable: collections.Iterable[Item]) -> None:
1082        self._it = iter(iterable)
def clone(self) -> Iter[~Item]:
1084    def clone(self) -> Iter[Item]:
1085        """Return a copy of this iterator.
1086
1087        ```py
1088        it = Iter([1, 2, 3])
1089
1090        for i in it.clone():
1091            ...
1092
1093        # The actual iterator hasn't been exhausted.
1094        assert it.count() == 3
1095        ```
1096        """
1097        return Iter(copy.copy(self._it))

Return a copy of this iterator.

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

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

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

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

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

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

        return result

counter = Counter(start=0, stop=10)
for i in counter.map(lambda x: x * 2): # multiply each number
    ...
Implementations

This class implements Iterator in Rust.

@staticmethod
@typing.final
def default() -> sain.iter.Empty[~Item]:
177    @staticmethod
178    @typing.final
179    def default() -> Empty[Item]:
180        """Return the default iterator for this type. It returns an empty iterator.
181
182        Example
183        -------
184        ```py
185        it: Iterator[int] = Iter.default()
186        assert t.next().is_none()
187        ```
188        """
189        return Empty()

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

Example
it: Iterator[int] = Iter.default()
assert t.next().is_none()
@typing.final
def collect( self, *, cast: Callable[[~Item], ~OtherItem] | None = None) -> MutableSequence[~Item] | MutableSequence[~OtherItem]:
199    @typing.final
200    def collect(
201        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
202    ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]:
203        """Collects all items in the iterator into a sequence.
204
205        Example
206        -------
207        ```py
208        iterator = Iter(range(3))
209        iterator.collect()
210        # [0, 1, 2, 3]
211        iterator.collect(cast=str) # Map each element and collect it.
212        # ['0', '1', '2', '3']
213        ```
214
215        Parameters
216        ----------
217        cast: `T | None`
218            An optional type to cast the items into.
219            If not provided the items will be returned as it's original type.
220        """
221        if cast is not None:
222            return [cast(i) for i in self]
223
224        return [_ for _ in self]

Collects all items in the iterator into a sequence.

Example
iterator = Iter(range(3))
iterator.collect()
# [0, 1, 2, 3]
iterator.collect(cast=str) # Map each element and collect it.
# ['0', '1', '2', '3']
Parameters
  • cast (T | None): An optional type to cast the items into. If not provided the items will be returned as it's original type.
@typing.final
def collect_into(self, collection: 'Collector[Item]') -> None:
226    @typing.final
227    def collect_into(self, collection: Collector[Item]) -> None:
228        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
229
230        if `collection` is a `MutableSequence[Item]`, this iterator will call `extend` on it,
231        and if it was a `set[Item]`, this will call `update` on it.
232
233        Example
234        -------
235        ```py
236        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
237        uniques = set()
238        iterator.collect_into(uniques)
239        # assert uniques == {1, 2, 3, 4, 6}
240        ```
241
242        Parameters
243        ----------
244        collection: `MutableSequence[T]` | `set[T]`
245            The collection to extend the items in this iterator with.
246        """
247        if isinstance(collection, collections.MutableSequence):
248            collection.extend(_ for _ in self)
249        else:
250            collection.update(_ for _ in self)

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

if collection is a MutableSequence[Item], this iterator will call extend on it, and if it was a set[Item], this will call update on it.

Example
iterator = Iter([1, 1, 2, 3, 4, 2, 6])
uniques = set()
iterator.collect_into(uniques)
# assert uniques == {1, 2, 3, 4, 6}
Parameters
  • collection (MutableSequence[T] | set[T]): The collection to extend the items in this iterator with.
@typing.final
def to_vec(self) -> 'Vec[Item]':
252    @typing.final
253    def to_vec(self) -> Vec[Item]:
254        """Consume this iterator, returning all of its elements in a `Vec[T]`.
255
256        Example
257        -------
258        ```py
259        it = sain.iter.once(0)
260        vc = it.to_vec()
261
262        assert to_vec == [0]
263        ```
264        """
265        from sain.collections.vec import Vec
266
267        return Vec(_ for _ in self)

Consume this iterator, returning all of its elements in a Vec[T].

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

assert to_vec == [0]
@typing.final
def sink(self) -> None:
269    @typing.final
270    def sink(self) -> None:
271        """Consume all elements from this iterator, returning nothing.
272
273        Example
274        -------
275        ```py
276        it = Iter((1, 2, 3))
277        it.sink()
278        assert it.next().is_none()
279        ```
280        """
281        for _ in self:
282            pass

Consume all elements from this iterator, returning nothing.

Example
it = Iter((1, 2, 3))
it.sink()
assert it.next().is_none()
@typing.final
def raw_parts(self) -> Generator[~Item, None, None]:
284    @typing.final
285    def raw_parts(self) -> collections.Generator[Item, None, None]:
286        """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items.
287
288        ```py
289        it = Iter("cba")
290        sort = sorted(it.raw_parts())
291
292        assert it.count() == 0
293        assert sort == ["a", "b", "c"]
294        ```
295        """
296        for item in self:
297            yield item

Decompose this iterator into a Generator[Item] that yields all of the remaining items.

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

assert it.count() == 0
assert sort == ["a", "b", "c"]
def next(self) -> 'Option[Item]':
299    def next(self) -> Option[Item]:
300        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
301
302        Example
303        -------
304        ```py
305        iterator = Iter(["1", "2"])
306        assert iterator.next() == Some("1")
307        assert iterator.next() == Some("2")
308        assert iterator.next().is_none()
309        ```
310        """
311        try:
312            return _option.Some(self.__next__())
313        except StopIteration:
314            # SAFETY: No more items in the iterator.
315            return _option.NOTHING

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]:
317    def cloned(self) -> Cloned[Item]:
318        """Creates an iterator which shallow copies its elements by reference.
319
320        .. note::
321            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
322            on each item that is being yielded.
323
324        Example
325        -------
326        ```py
327        @dataclass
328        class User:
329            users_ids: list[int] = []
330
331        # An iterator which elements points to the same user.
332        user = User()
333        it = Iter((user, user))
334
335        for u in it.cloned():
336            u.user_ids.append(1)
337
338        # We iterated over the same user pointer twice and appended "1"
339        # since `copy` returns a shallow copy of nested structures.
340        assert len(user.user_ids) == 2
341        ```
342        """
343        return Cloned(self)

Creates an iterator which shallow copies its elements by reference.

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]:
345    def copied(self) -> Copied[Item]:
346        """Creates an iterator which copies all of its elements by value.
347
348        If you only need a copy of the item reference, Use `.cloned()` instead.
349
350        .. note::
351            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
352            on each item that is being yielded.
353
354        Example
355        -------
356        ```py
357        @dataclass
358        class User:
359            users_ids: list[int] = []
360
361        # An iterator which elements points to the same user.
362        user = User()
363        it = Iter((user, user))
364
365        for u in it.copied():
366            # A new list is created for each item.
367            u.user_ids.append(1)
368
369        # The actual list is untouched since we consumed a deep copy of it.
370        assert len(user.user_ids) == 0
371        ```
372        """
373        return Copied(self)

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

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

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

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

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

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

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

Maps each item in the iterator to another type.

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

for item in iterator:
    assert isinstance(item, int)
Parameters
  • predicate (Callable[[Item], OtherItem]): The function to map each item in the iterator to the other type.
def filter(self, predicate: Callable[[~Item], bool]) -> sain.iter.Filter[~Item]:
394    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
395        """Filters the iterator to only yield items that match the predicate.
396
397        Example
398        -------
399        ```py
400        places = Iter(['London', 'Paris', 'Los Angeles'])
401        for place in places.filter(lambda place: place.startswith('L')):
402            print(place)
403
404        # London
405        # Los Angeles
406        ```
407        """
408        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]:
410    def take(self, count: int) -> Take[Item]:
411        """Take the first number of items until the number of items
412        are yielded or the end of the iterator is exhausted.
413
414        Example
415        -------
416        ```py
417        iterator = Iter(['c', 'x', 'y'])
418
419        for x in iterator.take(2):
420            assert x in ('c', 'x')
421
422        # <Iter(['c', 'x'])>
423        ```
424        """
425        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]:
427    def skip(self, count: int) -> Skip[Item]:
428        """Skips the first number of items in the iterator.
429
430        Example
431        -------
432        ```py
433        iterator = Iter((1, 2, 3, 4))
434        for i in iterator.skip(2):
435            print(i)
436
437        # 3
438        # 4
439        ```
440        """
441        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]:
443    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
444        """Create a new iterator that yields a tuple of the index and item.
445
446        Example
447        -------
448        ```py
449        iterator = Iter([1, 2, 3])
450        for index, item in iterator.enumerate():
451            print(index, item)
452
453        # 0 1
454        # 1 2
455        # 2 3
456        ```
457        """
458        return Enumerate(self, start)

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

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

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

yields items from the iterator while predicate returns True.

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

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

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

Yields items from the iterator while predicate returns False.

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

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

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

Example
iter = Iter(['a', 'b', 'c', 'd', 'e'])
chunks = iter.chunks()
assert chunks.next().unwrap() == ['a', 'b']
assert chunks.next().unwrap() == ['c', 'd']
assert chunks.next().unwrap() == ['e']
assert chunks.next().is_none()
def all(self, predicate: Callable[[~Item], bool]) -> bool:
521    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
522        """Return `True` if all items in the iterator match the predicate.
523
524        Example
525        -------
526        ```py
527        iterator = Iter([1, 2, 3])
528        if iterator.all(lambda item: isinstance(item, int)):
529            print("yes")
530        ```
531
532        Parameters
533        ----------
534        predicate: `collections.Callable[[Item], bool]`
535            The function to test each item in the iterator.
536        """
537        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])
if iterator.all(lambda item: isinstance(item, int)):
    print("yes")
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
def any(self, predicate: Callable[[~Item], bool]) -> bool:
539    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
540        """`True` if any items in the iterator match the predicate.
541
542        Example
543        -------
544        ```py
545        iterator = Iter([1, 2, 3])
546        if iterator.any(lambda item: isinstance(item, int)):
547            print("At least one item is an int.")
548        # At least one item is an int.
549        ```
550
551        Parameters
552        ----------
553        predicate: `collections.Callable[[Item], bool]`
554            The function to test each item in the iterator.
555        """
556        return any(predicate(item) for item in self)

True if any items in the iterator match the predicate.

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

Zips the iterator with another iterable.

Example
xs = Iter([1, 2, 3])
ys = [4, 5, 6]

iter = xs.zip(ys)

assert iter.next().unwrap() == (1, 4)
assert iter.next().unwrap() == (2, 5)
assert iter.next().unwrap() == (3, 6)
Parameters
  • other (Iterable[OtherItem]): The iterable to zip with.
def sort( self, *, key: 'collections.Callable[[Item], SupportsRichComparison] | None' = None, reverse: bool = False) -> MutableSequence[~Item]:
583    def sort(
584        self,
585        *,
586        key: collections.Callable[[Item], SupportsRichComparison] | None = None,
587        reverse: bool = False,
588    ) -> collections.MutableSequence[Item]:
589        """Sorts the iterator elements and return it in a mutable sequence.
590
591        Example
592        -------
593        ```py
594        iterator = Iter([3, 1, 6, 7])
595        for item in iterator.sort(key=lambda item: item < 3):
596            print(item)
597        # 1
598        # 3
599        # 6
600        # 7
601        ```
602
603        Parameters
604        ----------
605        key: `collections.Callable[[Item], Any]`
606            The function to sort by.
607        reverse: `bool`
608            Whether to reverse the sort.
609        """
610        return sorted([_ for _ in self], key=key, reverse=reverse)  # pyright: ignore - key can be None here

Sorts the iterator elements and return it in a mutable sequence.

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) -> Iter[~Item]:
612    def reversed(self) -> Iter[Item]:
613        """Returns a new iterator that yields the items in the iterator in reverse order.
614
615        This consumes this iterator into a sequence and return a new iterator containing all of the elements
616        in reversed order.
617
618        Example
619        -------
620        ```py
621        iterator = Iter([3, 1, 6, 7])
622        for item in iterator.reversed():
623            print(item)
624        # 7
625        # 6
626        # 1
627        # 3
628        ```
629        """
630        # NOTE: In order to reverse the iterator we need to
631        # first collect it into some collection.
632        return Iter(reversed([_ for _ in self]))

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

This consumes this iterator into a sequence and return a new iterator containing all of the elements in reversed order.

Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.reversed():
    print(item)
# 7
# 6
# 1
# 3
def union(self, other: Iterable[~Item]) -> Iter[~Item]:
634    def union(self, other: collections.Iterable[Item]) -> Iter[Item]:
635        """Returns a new iterator that yields all items from both iterators.
636
637        Example
638        -------
639        ```py
640        iterator = Iter([1, 2, 3])
641        other = [4, 5, 6]
642
643        for item in iterator.union(other):
644            print(item)
645        # 1
646        # 2
647        # 3
648        # 4
649        # 5
650        # 6
651        ```
652
653        Parameters
654        ----------
655        other: `Iter[Item]`
656            The iterable to union with.
657        """
658        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]':
660    def first(self) -> Option[Item]:
661        """Returns the first item in the iterator.
662
663        Example
664        -------
665        ```py
666        iterator = Iter([3, 1, 6, 7])
667        iterator.first().is_some_and(lambda x: x == 3)
668        ```
669        """
670        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]':
672    def last(self) -> Option[Item]:
673        """Returns the last item in the iterator.
674
675        Example
676        -------
677        ```py
678        iterator = Iter([3, 1, 6, 7])
679        iterator.last().is_some_and(lambda x: x == 7)
680        ```
681        """
682        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:
684    def count(self) -> int:
685        """Consume this iterator, returning the count of elements it has.
686
687        Example
688        -------
689        ```py
690        it = Iter(range(3))
691        assert it.count() == 3
692        ```
693        """
694        count = 0
695        for _ in self:
696            count += 1
697
698        return count

Consume this iterator, returning the count of elements it has.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Example
a = Iter([1, 2, 3, 4])
sum = a.fold(0, lambda acc, elem: acc + elem)
assert sum == 10
def advance_by(self, n: int) -> '_result.Result[None, int]':
778    def advance_by(self, n: int) -> _result.Result[None, int]:
779        """Advances the iterator by `n` elements.
780
781        Returns `Result[None, int]`, where `Ok(None)` means the iterator
782        advanced successfully, and `Err(int)` if `None` encountered, where `int`
783        represents the remaining number of steps that could not be advanced because the iterator ran out.
784
785        Example
786        -------
787        ```py
788        it = into_iter([1, 2, 3, 4])
789        assert it.advance_by(2).is_ok()
790        assert it.next() == Some(3)
791        assert it.advance_by(0).is_ok()
792        assert it.advance_by(100) == Err(99)
793        ```
794        """
795        for i in range(n):
796            try:
797                self.__next__()
798            except StopIteration:
799                return _result.Err(n - i)
800
801        return _result.Ok(None)

Advances the iterator by n elements.

Returns Result[None, int], where Ok(None) means the iterator advanced successfully, and Err(int) if None encountered, where int represents the remaining number of steps that could not be advanced because the iterator ran out.

Example
it = into_iter([1, 2, 3, 4])
assert it.advance_by(2).is_ok()
assert it.next() == Some(3)
assert it.advance_by(0).is_ok()
assert it.advance_by(100) == Err(99)
def nth(self, n: int) -> 'Option[Item]':
803    def nth(self, n: int) -> Option[Item]:
804        """Returns the `n`th element of the iterator
805
806        Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first
807        value.
808
809        Note all elements before `n` will be skipped / consumed.
810
811        Example
812        -------
813        ```py
814        a = into_iter([1, 2, 3])
815        assert a.iter().nth(1) == Some(2)
816        ```
817        """
818        for _ in range(n):
819            try:
820                self.__next__()
821            except StopIteration:
822                return _option.NOTHING
823
824        return self.next()

Returns the nth element of the iterator

Just like normal indexing, the count n starts from zero, so nth(0) returns the first value.

Note all elements before n will be skipped / consumed.

Example
a = into_iter([1, 2, 3])
assert a.iter().nth(1) == Some(2)
def sum(self: 'Sum') -> int:
826    def sum(self: Sum) -> int:
827        """Sums an iterator of a possible type `T` that can be converted to an integer.
828
829        where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`).
830
831        Example
832        -------
833        ```py
834        numbers: Iterator[str] = Iter(["1", "2", "3"])
835        total = numbers.sum()
836        assert total == 6
837        ```
838        """
839        return sum(int(_) for _ in self)

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

where T is a typeof (int, float, str, ReadableBuffer, SupportsTrunc, SupportsIndex).

Example
numbers: Iterator[str] = Iter(["1", "2", "3"])
total = numbers.sum()
assert total == 6
def for_each(self, func: Callable[[~Item], typing.Any]) -> None:
841    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
842        """Calls `func` on each item in the iterator.
843
844        Example
845        -------
846        ```py
847        iterator = Iter([1, 2, 3])
848        iterator.for_each(lambda item: print(item))
849        # 1
850        # 2
851        # 3
852        ```
853
854        Parameters
855        ----------
856        func: `collections.Callable[[Item], typing.Any]`
857            The function to call on each item in the iterator.
858        """
859        for item in self:
860            func(item)

Calls func on each item in the iterator.

Example
iterator = Iter([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
  • func (collections.Callable[[Item], typing.Any]): The function to call on each item in the iterator.
async def async_for_each( self, func: Callable[[~Item], Coroutine[typing.Any, typing.Any, ~OtherItem]]) -> '_result.Result[collections.Sequence[OtherItem], futures.JoinError]':
862    async def async_for_each(
863        self,
864        func: collections.Callable[
865            [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem]
866        ],
867    ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]:
868        """Calls the async function on each item in the iterator *concurrently*.
869
870        Concurrently meaning that the next item will not wait for other items
871        to finish to execute, each item gets called in a separate task.
872
873        After all the tasks finish, a `Result[list[T], JoinError]` will be returned,
874        which will need to be handled by the caller.
875
876        Example
877        -------
878        ```py
879        async def create_user(username: str) -> None:
880            await aiohttp.request("POST", f'.../{username}')
881
882        async def main():
883            users = sain.into_iter(["Danny", "Flower"])
884            match await users.async_for_each(lambda username: create_user(username)):
885                case Ok(result):
886                    # all good
887                case Err(why):
888                    print(f"couldn't gather all futures, err={why}")
889        ```
890
891        Parameters
892        ----------
893        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
894            The async function to call on each item in the iterator.
895        """
896        return await futures.join(*(func(item) for item in self))

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

Concurrently meaning that the next item will not wait for other items to finish to execute, each item gets called in a separate task.

After all the tasks finish, a Result[list[T], JoinError] will be returned, which will need to be handled by the caller.

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

async def main():
    users = sain.into_iter(["Danny", "Flower"])
    match await users.async_for_each(lambda username: create_user(username)):
        case Ok(result):
            # all good
        case Err(why):
            print(f"couldn't gather all futures, err={why}")
Parameters
  • func (collections.Callable[[Item], Coroutine[None, Any, Any]]): The async function to call on each item in the iterator.
@rustc_diagnostic_item('todo')
def todo(message: 'LiteralString | None' = None) -> Never:
744@rustc_diagnostic_item("todo")
745def todo(message: LiteralString | None = None) -> typing_extensions.Never:
746    """A place holder that indicates unfinished code.
747
748    Example
749    -------
750    ```py
751    from sain import todo
752
753    def from_json(payload: dict[str, int]) -> int:
754        # Calling this function will raise `RuntimeError`.
755        todo()
756    ```
757
758    Parameters
759    ----------
760    message : `str | None`
761        Multiple optional arguments to pass if the error was raised.
762    """
763    raise RuntimeError(
764        f"not yet implemented: {message}" if message else "not yet implemented"
765    )

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 `RuntimeError`.
    todo()
Parameters
  • message (str | None): Multiple optional arguments to pass if the error was raised.
Implementations

This function implements todo in Rust.

@rustc_diagnostic_item('deprecated')
def deprecated( *, since: "typing.Literal['CURRENT_VERSION'] | LiteralString | None" = None, removed_in: 'LiteralString | None' = None, use_instead: 'LiteralString | None' = None, hint: 'LiteralString | None' = None):
671@rustc_diagnostic_item("deprecated")
672def deprecated(
673    *,
674    since: typing.Literal["CURRENT_VERSION"] | LiteralString | None = None,
675    removed_in: LiteralString | None = None,
676    use_instead: LiteralString | None = None,
677    hint: LiteralString | None = None,
678):
679    """A decorator that marks a function as deprecated.
680
681    Has no runtime side-effects.
682
683    Example
684    -------
685    ```py
686    from sain import deprecated
687
688    @deprecated(
689        since = "1.0.0",
690        removed_in ="3.0.0",
691        use_instead = "UserImpl()",
692        hint = "Hint for ux."
693    )
694    class User:
695        # calling the decorator is not necessary.
696        @deprecated
697        def username(self) -> str:
698            ...
699
700    user = User() # This will cause a warning at runtime.
701
702    ```
703
704    Parameters
705    ----------
706    since : `str`
707        The version that the function was deprecated. the `CURRENT_VERSION` is used internally only.
708    removed_in : `str | None`
709        If provided, It will log when will the object will be removed in.
710    use_instead : `str | None`
711        If provided, This should be the alternative object name that should be used instead.
712    hint: `str`
713        An optional hint for the user.
714    """
715
716    def _create_message() -> LiteralString:
717        msg = ""
718
719        if since is not None:
720            if since == "CURRENT_VERSION":
721                from ._misc import __version__
722
723                msg += " since " + __version__
724            else:
725                msg += " since " + since
726
727        if removed_in:
728            msg += f" Scheduled for removal in `{removed_in}`."
729
730        if use_instead is not None:
731            msg += f" Use `{use_instead}` instead."
732
733        if hint:
734            msg += f" Hint: {hint}"
735        return msg
736
737    msg = _create_message()
738    if sys.version_info >= (3, 13, 0):
739        return warnings.deprecated(msg, stacklevel=3, category=None)
740
741    return typing_extensions.deprecated(msg, stacklevel=3, category=None)

A decorator that marks a function as deprecated.

Has no runtime side-effects.

Example
from sain import deprecated

@deprecated(
    since = "1.0.0",
    removed_in ="3.0.0",
    use_instead = "UserImpl()",
    hint = "Hint for ux."
)
class User:
    # calling the decorator is not necessary.
    @deprecated
    def username(self) -> str:
        ...

user = User() # This will cause a warning at runtime.
Parameters
  • since (str): The version that the function was deprecated. the CURRENT_VERSION is used internally only.
  • removed_in (str | None): If provided, It will log when will the object will be removed in.
  • use_instead (str | None): If provided, This should be the alternative object name that should be used instead.
  • hint (str): An optional hint for the user.
Implementations

This function implements deprecated in Rust.

@rustc_diagnostic_item('unimplemented')
def unimplemented(message: 'LiteralString | None' = None, /) -> Never:
768@rustc_diagnostic_item("unimplemented")
769def unimplemented(message: LiteralString | None = None, /) -> typing_extensions.Never:
770    """Marks an object as unimplemented.
771
772    Raises `RuntimeError` if called.
773
774    Example
775    -------
776    ```py
777    from sain import unimplemented
778
779    class User:
780        unimplemented()
781
782    match parse_http_method(inp):
783        case "GET": ...
784        case _: unimplemented("not yet")
785    ```
786
787    Parameters
788    ----------
789    message : `str | None`
790        An optional message to be displayed when the function is called. Otherwise default message will be used.
791    """
792    raise RuntimeError(
793        "not implemented" if not message else f"not implemented: {message}"
794    ) from None

Marks an object as unimplemented.

Raises RuntimeError if called.

Example
from sain import unimplemented

class User:
    unimplemented()

match parse_http_method(inp):
    case "GET": ...
    case _: unimplemented("not yet")
Parameters
  • message (str | None): An optional message to be displayed when the function is called. Otherwise default message will be used.
Implementations

This function implements unimplemented in Rust.

@rustc_diagnostic_item('doc')
def doc( path: 'Read') -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
797@rustc_diagnostic_item("doc")
798def doc(
799    path: Read,
800) -> collections.Callable[
801    [collections.Callable[P, U]],
802    collections.Callable[P, U],
803]:
804    """Set `path` to be the object's documentation.
805
806    Example
807    -------
808    ```py
809    from sain import doc
810    from pathlib import Path
811
812    @doc(Path("../README.md"))
813    class builtins:
814        @doc("bool.html")
815        def bool_docs() -> None:
816            ...
817    ```
818
819    Parameters
820    ----------
821    path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]`
822        The path to read the content from.
823    """
824
825    def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]:
826        with open(path, "r") as file:
827            f.__doc__ = file.read()
828
829        return lambda *args, **kwargs: f(*args, **kwargs)
830
831    return decorator

Set path to be the object's documentation.

Example
from sain import doc
from pathlib import Path

@doc(Path("../README.md"))
class builtins:
    @doc("bool.html")
    def bool_docs() -> None:
        ...
Parameters
  • path (type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]): The path to read the content from.
Implementations

This function implements doc in Rust.

@rustc_diagnostic_item('include_str')
def include_str(file: 'LiteralString') -> 'LiteralString':
549@rustc_diagnostic_item("include_str")
550def include_str(file: LiteralString) -> LiteralString:
551    """Includes a file as literal `str`.
552
553    This function is not magic, It is literally defined as
554
555    ```py
556    with open(file, "r") as f:
557        return f.read()
558    ```
559
560    The file name can may be either a relative to the current file or a complete path.
561
562    Example
563    -------
564    ```py
565    from sain.macros import include_str
566
567    def entry() -> None:
568        ...
569
570    entry.__doc__ = include_str("README.md")
571
572    ```
573    """
574    with open(file, "r") as buf:
575        return buf.read()  # pyright: ignore - simulates a `&'static str` slice.

Includes a file as literal str.

This function is not magic, It is literally defined as

with open(file, "r") as f:
    return f.read()

The file name can may be either a relative to the current file or a complete path.

Example
from sain.macros import include_str

def entry() -> None:
    ...

entry.__doc__ = include_str("README.md")
Implementations

This function implements include_str in Rust.

@rustc_diagnostic_item('include_bytes')
def include_bytes(file: 'LiteralString') -> bytes:
519@rustc_diagnostic_item("include_bytes")
520def include_bytes(file: LiteralString) -> bytes:
521    """Includes a file as `bytes`.
522
523    This function is not magic, It is literally defined as
524
525    ```py
526    with open(file, "rb") as f:
527        return f.read()
528    ```
529
530    The file name can may be either a relative to the current file or a complete path.
531
532    Example
533    -------
534    File "spanish.in":
535    ```text
536    adiós
537    ```
538    File "main.py":
539    ```py
540    from sain.macros import include_bytes
541    buffer = include_bytes("spanish.in")
542    assert buffer.decode() == "adiós"
543    ```
544    """
545    with open(file, "rb") as buf:
546        return buf.read()

Includes a file as bytes.

This function is not magic, It is literally defined as

with open(file, "rb") as f:
    return f.read()

The file name can may be either a relative to the current file or a complete path.

Example

File "spanish.in":

adiós

File "main.py":

from sain.macros import include_bytes
buffer = include_bytes("spanish.in")
assert buffer.decode() == "adiós"
Implementations

This function implements include_bytes in Rust.

@rustc_diagnostic_item('assert_eq')
def assert_eq(left: +T, right: +T) -> None:
479@rustc_diagnostic_item("assert_eq")
480def assert_eq(left: T, right: T) -> None:
481    """Asserts that two expressions are equal to each other.
482
483    This exactly as `assert left == right`, but includes a useful message in case of failure.
484
485    Example
486    -------
487    ```py
488    from sain.macros import assert_eq
489    a = 3
490    b = 1 + 2
491    assert_eq(a, b)
492    ```
493    """
494    assert left == right, (
495        f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"'
496    )

Asserts that two expressions are equal to each other.

This exactly as assert left == right, but includes a useful message in case of failure.

Example
from sain.macros import assert_eq
a = 3
b = 1 + 2
assert_eq(a, b)
Implementations

This function implements assert_eq in Rust.

@rustc_diagnostic_item('assert_ne')
def assert_ne(left: +T, right: +T) -> None:
499@rustc_diagnostic_item("assert_ne")
500def assert_ne(left: T, right: T) -> None:
501    """Asserts that two expressions are not equal to each other.
502
503    This exactly as `assert left == right`, but includes a useful message in case of failure.
504
505    Example
506    -------
507    ```py
508    from sain.macros import assert_ne
509    a = 3
510    b = 2 + 2
511    assert_ne(a, b)
512    ```
513    """
514    assert left != right, (
515        f'assertion `left != right` failed\nleft: "{left!r}"\nright: "{right!r}"'
516    )

Asserts that two expressions are not equal to each other.

This exactly as assert left == right, but includes a useful message in case of failure.

Example
from sain.macros import assert_ne
a = 3
b = 2 + 2
assert_ne(a, b)
Implementations

This function implements assert_ne in Rust.

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

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

Implementations

This class implements .Result.html#variant.Ok">Ok in Rust.

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

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

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

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

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

Returns True if the contained value is Err.

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

assert value.is_err() == False
def is_err_and(self, f: Callable[[~T], bool]) -> Literal[False]:
169    def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]:
170        """Returns `True` if the contained value is `Ok` and `f()` returns True.
171
172        Example
173        -------
174        ```py
175        value: Result[str, None] = Ok("value")
176
177        assert value.is_err_and(lambda inner: inner == "value")
178        # False
179        ```
180        """
181        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:
187    def expect(self, message: str, /) -> T:
188        """Return the underlying value if it was `Ok`, Raising `RuntimeError`
189        if it was `Err` with `message` passed to it.
190
191        Example
192        -------
193        ```py
194        ok: Result[str, None] = Ok("owo")
195        ok.expect("err") # owo
196
197        err: Result[str, None] = Err(None)
198        err.expect("err") # RuntimeError("err")
199        ```
200        """
201        return self._inner

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

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

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

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

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

err: Result[str, None] = Err(None)
err.expect_err() # None
def unwrap(self) -> ~T:
218    def unwrap(self) -> T:
219        """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`.
220
221        Example
222        -------
223        ```py
224        ok: Result[str, None] = Ok("owo")
225        ok.unwrap() # owo
226
227        err: Result[str, None] = Err(None)
228        err.unwrap() # RuntimeError
229        ```
230        """
231        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:
233    def unwrap_or(self, default: T, /) -> T:
234        """Return the underlying value if it was `Ok`, returning `default` if it was `Err`.
235
236        Example
237        -------
238        ```py
239        ok: Result[str, None] = Ok("OwO")
240        ok.unwrap_or("uwu") # OwO
241
242        err: Result[str, None] = Err(None)
243        err.unwrap_or("uwu") # uwu
244        ```
245        """
246        return self._inner

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

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

err: Result[str, None] = Err(None)
err.unwrap_or("uwu") # uwu
def unwrap_or_else(self, f: Callable[[~E], ~T]) -> ~T:
248    def unwrap_or_else(self, f: F[E, T]) -> T:
249        """Return the contained `Ok` value or computes it from `f()` if it was `Err`.
250
251        Example
252        -------
253        ```py
254        ok: Result[int, str] = Ok(4)
255        ok.unwrap_or_else(lambda e: 0) # 4
256
257        err: Result[int, str] = Err("word")
258        err.unwrap_or_else(lambda e: len(e)) # 4
259        ```
260        """
261        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) -> Never:
263    def unwrap_err(self) -> Never:
264        """Return the contained `Err` value, Raising if it was `Ok`.
265
266        Example
267        -------
268        ```py
269        ok: Result[str, None] = Ok("buh")
270        ok.unwrap_err()  # RuntimeError
271
272        err: Result[str, None] = Err(None)
273        err.unwrap_err() == None
274        # True
275        ```
276        """
277        raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}")

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

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

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

Transform Result[T, E] to Option[T], mapping Ok(v) to Some(T) and Err(e) to None.

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

value: Result[str, int] = Err(0)
value.ok().is_none() # True
def err(self) -> 'Option[T]':
298    def err(self) -> Option[T]:
299        """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`.
300
301        Example
302        -------
303        ```py
304        value: Result[str, None] = Ok("buh")
305        value.err().is_none() # True
306
307        value: Result[str, int] = Err(0)
308        value.err().is_some() # True
309        ```
310        """
311        return _option.NOTHING

Transform Result[T, E] to Option[E], mapping Ok(v) to None and Err(e) to Some(e).

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

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

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

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

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

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

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

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

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

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

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

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

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

Returns the provided default if the contained value is Err,

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

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

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

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

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

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

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

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

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

An iterator over the possible contained value.

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

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

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

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

Implementations

This class implements .Result.html#variant.Err">Err in Rust.

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

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

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

assert value.is_ok() == False
def is_ok_and(self, f: Callable[[~E], bool]) -> Literal[False]:
485    def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]:
486        """Returns `True` if the contained value is `Ok` and `f()` returns True.
487
488        Example
489        -------
490        ```py
491        value: Result[str, None] = Err(None)
492
493        assert value.is_ok_and(lambda inner: inner == "value")
494        # False
495        ```
496        """
497        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]:
500    def is_err(self) -> typing.Literal[True]:
501        """Returns `True` if the contained value is `Err`.
502
503        Example
504        -------
505        ```py
506        value: Result[str, None] = Err(None)
507
508        assert value.is_err() == True
509        ```
510        """
511        return True

Returns True if the contained value is Err.

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

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

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

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

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

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

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

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

Return the Err if it was Err, panicking otherwise.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

x: Result[str, str] = Err("err")
x.inspect_err(sink) # Called err
def ok(self) -> 'Option[None]':
669    def ok(self) -> Option[None]:
670        """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`.
671
672        Example
673        -------
674        ```py
675        value: Result[str, None] = Ok("buh")
676        value.ok().is_some() # True
677
678        value: Result[str, int] = Err(0)
679        value.ok().is_none() # True
680        ```
681        """
682        return _option.NOTHING

Transform Result[T, E] to Option[T], mapping Ok(v) to Some(T) and Err(e) to None.

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

value: Result[str, int] = Err(0)
value.ok().is_none() # True
def err(self) -> 'Option[E]':
684    def err(self) -> Option[E]:
685        """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`.
686
687        Example
688        -------
689        ```py
690        value: Result[str, None] = Ok("buh")
691        value.err().is_none() # True
692
693        value: Result[str, int] = Err(0)
694        value.err().is_some() # True
695        ```
696        """
697        return _option.Some(self._inner)

Transform Result[T, E] to Option[E], mapping Ok(v) to None and Err(e) to Some(e).

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

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

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

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

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

Returns the provided default if the contained value is Err,

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

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

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

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

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

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

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

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

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

An iterator over the possible contained value.

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

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

c: Result[str, int] = Err(0)
c.iter().next() == Some(None)
Result = 'Ok[T] | Err[E]'
@rustc_diagnostic_item('Vec')
@typing.final
class Vec(sain.collections.slice.SliceMut[~T], collections.abc.MutableSequence[~T]):
162@rustc_diagnostic_item("Vec")
163@typing.final
164class Vec(SliceMut[T], collections.MutableSequence[T]):
165    """A basic implementation of Rust's `Vec<T>` type, backed by Python `list<T>`.
166
167    See the module top level documentation for more information.
168
169    Example
170    -------
171    ```py
172    names = Vec[str]()
173
174    names.push('foo')
175    names.push('bar')
176
177    print(names) # ['foo', 'bar']
178    assert names.len() == 2
179    ```
180    """
181
182    __slots__ = ("_buf", "_capacity")
183
184    @typing.overload
185    def __init__(self) -> None:
186        """Create an empty `Vec<T>`.
187
188        Example
189        ------
190        ```py
191        vec: Vec[float] = Vec()
192        ```
193        """
194
195    @typing.overload
196    def __init__(self, iterable: collections.Iterable[T]) -> None:
197        """Create a new `Vec<T>` from an iterable.
198
199        Example
200        -------
201        ```py
202        vec = Vec([1.2, 3.4])
203        ```
204        """
205
206    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
207        if isinstance(iterable, list):
208            # Calling `list()` on another list will copy it, So instead we just point to it.
209            self._buf = iterable
210        elif isinstance(iterable, Vec):
211            self._buf = iterable._buf
212        # any other iterable that ain't a list needs to get copied into a new list.
213        else:
214            self._buf = list(iterable) if iterable else []
215
216        self._capacity: int | None = None
217
218    @classmethod
219    def with_capacity(cls, capacity: int) -> Vec[T]:
220        """Create a new `Vec` with at least the specified capacity.
221        This vec will be able to hold `capacity` elements without pushing further.
222
223        The capacity may dynamically grow if `Vec.reserve` is called at runtime.
224
225        Check out `Vec.push_within_capacity` as well.
226
227        Example
228        -------
229        ```py
230        vec = Vec.with_capacity(3)
231        assert vec.len() == 0 and vec.capacity() >= 3
232
233        vec.push(1)
234        vec.push(2)
235        vec.push(3)
236        print(vec.len()) # 3
237
238        # This won't push.
239        vec.push(4)
240        ```
241        """
242        v = cls()
243        v._capacity = capacity
244        return v
245
246    def as_slice(self) -> Slice[T]:
247        """Return an immutable view over this vector elements.
248
249        No copying occurs.
250
251        Example
252        -------
253        ```py
254        def send_bytes(v: Slice[int]) -> None:
255            # access `vec` in a read-only mode.
256            socket.write_all(v)
257
258        buf = Vec([1, 2, 3])
259        send_bytes(buf.as_slice())
260        ```
261        """
262        # NOTE: Although, we could just return `self`, but,
263        # we want to return an exclusive view.
264        return Slice(self._buf)
265
266    def as_mut_slice(self) -> SliceMut[T]:
267        """Return a mutable view over this vector elements.
268
269        Example
270        -------
271        ```py
272        def read_bytes(buf: SliceMut[int]) -> None:
273            # similar to how `Write::write_to_end` requires a `&mut [u8]`
274            socket.read_to_end(buf)
275
276        buf: Vec[int] = Vec()
277        read_bytes(buf.as_mut_slice()) # or just `buf`
278        assert buf.len() >= 1
279        ```
280        """
281        return self
282
283    def capacity(self) -> int:
284        """Return the capacity of this vector if set, 0 if not .
285
286        The number `0` here has two different Meanings:
287        - The first means the `Vec` doesn't have a specific capacity set.
288        - The second means the `Vec` literally initialized with `0` capacity.
289
290        Example
291        -------
292        ```py
293        vec_with_cap = Vec.with_capacity(3)
294        assert vec_with_cap.capacity().unwrap() == 3
295
296        vec = Vec([1, 2, 3])
297        assert vec.capacity() == 0
298        ```
299        """
300        return 0 if self._capacity is None else self._capacity
301
302    def leak(self) -> list[T]:
303        """Consumes and leaks the Vec, returning a mutable reference to the contents.
304
305        After calling this, this vec will no longer reference the underlying buffer,
306        therefore, it becomes unusable.
307
308        if `self` is uninitialized, an empty list is returned.
309
310        This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without
311        any copies.
312
313        Example
314        -------
315        ```py
316        x = Vec([1, 2, 3])
317        owned = x.leak()
318        # x is now unusable.
319        owned[0] += 1
320        assert owned == [2, 2, 3]
321        ```
322        """
323        # don't point to `_buf` anymore.
324        tmp = self._buf
325        del self._buf
326        return tmp  # pyright: ignore[reportReturnType] - _buf is a list.
327
328    def split_off(self, at: int) -> Vec[T]:
329        """Split the vector off at the specified position.
330
331        Returns a new vector at the range of `[at : len]`, leaving `self` at `[0: at]`.
332
333        if this vec is empty, `self` is returned unchanged.
334
335        Example
336        -------
337        ```py
338        origin = Vec((1, 2, 3, 4))
339        split = vec.split_off(2)
340
341        print(origin, split)  # [1, 2], [3, 4]
342        ```
343
344        Raises
345        ------
346        `IndexError`
347            This method will raise if `at` > `len(self)`
348        """
349        len_ = self.len()
350        if at > len_:
351            raise IndexError(
352                f"Index `at` ({at}) should be <= than len of vector ({len_}) "
353            ) from None
354
355        # Either the list is empty or uninit.
356        if not self._buf:
357            return self
358
359        split = self._buf[at:len_]
360        del self._buf[at:len_]
361        return Vec(split)
362
363    def truncate(self, size: int) -> None:
364        """Shortens the vec, keeping the first `size` elements and dropping the rest.
365
366        Example
367        -------
368        ```py
369        vec = Vec([1,2,3])
370        vec.truncate(1)
371        assert vec == [1]
372        ```
373        """
374        del self._buf[size:]
375
376    def retain(self, f: collections.Callable[[T], bool]) -> None:
377        """Retains only the elements specified by the predicate.
378
379        This operation occurs in-place without copying the original list.
380
381        Example
382        -------
383        ```py
384        vec = Vec([1, 2, 3])
385        vec.retain(lambda elem: elem > 1)
386
387        assert vec == [2, 3]
388        ```
389
390        The impl for this method is very simple
391        ```py
392        for idx, e in enumerate(vec):
393            if not f(e):
394                del vec[idx]
395        ```
396        """
397        idx = 0
398        while idx < len(self._buf):
399            if not f(self._buf[idx]):
400                del self._buf[idx]
401            else:
402                idx += 1
403
404    def swap_remove(self, item: T) -> T:
405        """Remove the first appearance of `item` from this vector and return it.
406
407        Raises
408        ------
409        `ValueError`
410            if `item` is not in this vector.
411
412        Example
413        -------
414        ```py
415        vec = Vec(('a', 'b', 'c'))
416        element = vec.swap_remove('a')
417        assert vec == ['b', 'c'] and element == 'a'
418        ```
419        """
420        return self._buf.pop(self.index(item))
421
422    def push(self, item: T) -> None:
423        """Push an element at the end of the vector.
424
425        Example
426        -------
427        ```py
428        vec = Vec()
429        vec.push(1)
430
431        assert vec == [1]
432        ```
433        """
434        if (cap := self._capacity) is not None and len(self._buf) == cap:
435            # max cap reached.
436            return
437
438        self._buf.append(item)
439
440    def push_within_capacity(self, x: T) -> Result[None, T]:
441        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
442
443        Example
444        -------
445        ```py
446        vec: Vec[int] = Vec.with_capacity(3)
447        for i in range(3):
448            match vec.push_within_capacity(i):
449                case Ok(_):
450                    print("All good.")
451                case Err(elem):
452                    print("Reached max cap :< can't push", elem)
453        ```
454
455        Or you can also just call `Vec.push` and it will push if there's is sufficient capacity.
456        ```py
457        vec: Vec[int] = Vec.with_capacity(3)
458
459        vec.extend((1, 2, 3))
460        vec.push(4)
461
462        assert vec.len() == 3
463        ```
464        """
465        if self.len() == self._capacity:
466            return _result.Err(x)
467
468        self._buf.append(x)
469        return _result.Ok(None)
470
471    def reserve(self, additional: int) -> None:
472        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
473
474        Example
475        -------
476        ```py
477        vec = Vec.with_capacity(3)
478
479        for i in range(3):
480            vec.push(i)
481
482        match vec.push_within_capacity(4):
483            case Ok(_):
484                print("Pushed 4 successfully.")
485            case Err(elem):
486                vec.reserve(1)  # Reserve capacity for 1 more element.
487                vec.push(4)  # Now we can push 4.
488        ```
489        """
490        if self._capacity is not None:
491            self._capacity += additional
492
493    def shrink_to_fit(self) -> None:
494        """Shrink the capacity of this `Vec` to match its length.
495
496        If `self` is initialized with no capacity, This is a `NOP`.
497
498        Example
499        -------
500        ```py
501        s = Vec([1, 2, 3, 4])
502
503        s.reserve(100)
504        s.shrink_to_fit()
505        assert s.capacity() == 4
506        ```
507        """
508        if self._capacity is None:
509            return
510
511        # The capacity is never less than the length.
512        self._capacity = min(self.__len__(), self._capacity)
513
514    def shrink_to(self, min_capacity: int) -> None:
515        """Shrink the capacity of this `Vec` to a minimum specified capacity.
516
517        If `self` is initialized with no capacity or the current capacity is less than the lower limit,
518        This is a `NOP`.
519
520        Example
521        -------
522        ```py
523        vec = Vec.with_capacity(10)
524        vec.extend([1, 2, 3])
525        assert vec.capacity() >= 10
526        vec.shrink_to(4)
527        assert vec.capacity() >= 4
528        vec.shrink_to(0)
529        ```
530        """
531        if self._capacity is None or self._capacity <= min_capacity:
532            return
533
534        # Ensure the capacity is not reduced below the current length of the vector.
535        self._capacity = max(self.__len__(), min_capacity)
536
537    ##########################
538    # * Builtin Operations *
539    ##########################
540
541    def append(self, value: T) -> None:
542        """Append an element at the end of the vector.
543
544        An alias to `Vec.push`.
545        """
546        self._buf.append(value)
547
548    def insert(self, index: int, value: T) -> None:
549        """Insert an element at the position `index`.
550
551        Example
552        --------
553        ```py
554        vec = Vec((2, 3))
555        vec.insert(0, 1)
556        assert vec == [1, 2, 3]
557        ```
558        """
559        self[index] = value
560
561    def pop(self, index: int = -1) -> _option.Option[T]:
562        """Removes the last element from the vector and returns it, or `None` if it is empty.
563
564        Example
565        -------
566        ```py
567        vec = Vec((1, 2, 3))
568        assert vec.pop() == Some(3)
569        assert vec == [1, 2]
570        ```
571        """
572        if not self._buf:
573            return _option.NOTHING
574
575        return _option.Some(self._buf.pop(index))
576
577    def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]:
578        """Removes the last element from a vector and returns it if `f` returns `True`,
579        or `None` if it is empty.
580
581        Example
582        -------
583        ```py
584        vec = Vec((1, 2, 3))
585        assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
586        assert vec == [1, 2]
587        ```
588        """
589        if not self._buf:
590            return _option.NOTHING
591
592        if pred(self[-1]):
593            return self.pop()
594
595        return _option.NOTHING
596
597    def dedup(self) -> None:
598        """Removes consecutive repeated elements in the vector according to their `__eq__`
599        implementation.
600
601        Example
602        -------
603        ```py
604        vec = Vec([1, 2, 2, 3, 2])
605        vec.dedup()
606        assert vec == [1, 2, 3, 2]
607        """
608        self.dedup_by(lambda a, b: a == b)
609
610    def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None:
611        """Removes all but the first of consecutive elements in the vector satisfying a given equality.
612
613        Example
614        -------
615        ```py
616        vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
617        vec.dedup_by(lambda a, b: a.lower() == b.lower())
618        assert vec == ["foo", "bar", "baz", "bar"]
619        ```
620
621        Only remove consecutive duplicates.
622        ```py
623        vec = Vec([1, 2, 2, 3, 4, 1])
624        vec.dedup_by(lambda a, b: a == b)
625        # The final 1 is not adjacent to the first 1, so it is not removed.
626        assert vec == [1, 2, 3, 4, 1]
627        ```
628        """
629
630        if not self._buf or (len_ := len(self._buf)) <= 1:
631            return
632
633        idx = 1
634        while idx < len_:
635            if same_bucket(self._buf[idx], self._buf[idx - 1]):
636                del self._buf[idx]
637                len_ -= 1
638            else:
639                idx += 1
640
641    def remove(self, item: T) -> None:
642        """Remove the first appearance of `item` from this vector.
643
644        Example
645        -------
646        ```py
647        vec = Vec(('a', 'b', 'c'))
648        vec.remove('a')
649        assert vec == ['b', 'c']
650        ```
651        """
652        self._buf.remove(item)
653
654    def extend(self, iterable: collections.Iterable[T]) -> None:
655        """Extend this vector from another iterable.
656
657        Example
658        -------
659        ```py
660        vec = Vec((1, 2, 3))
661        vec.extend((4, 5, 6))
662
663        assert vec == [1, 2, 3, 4, 5, 6]
664        ```
665        """
666        self._buf.extend(iterable)
667
668    def copy(self) -> Vec[T]:
669        """Copy all elements of `self` into a new vector.
670
671        Example
672        -------
673        ```py
674        original = Vec((1,2,3))
675        copy = original.copy()
676        copy.push(4)
677
678        print(original) # [1, 2, 3]
679        ```
680        """
681        return Vec(self._buf[:])
682
683    def clear(self) -> None:
684        """Clear all elements of this vector.
685
686        Example
687        -------
688        ```py
689        vec = Vec((1,2,3))
690        vec.clear()
691        assert vec.len() == 0
692        ```
693        """
694        self._buf.clear()
695
696    def sort(
697        self,
698        *,
699        key: collections.Callable[[T], SupportsRichComparison] | None = None,
700        reverse: bool = False,
701    ) -> None:
702        """This method sorts the list in place, using only < comparisons between items.
703
704        Example
705        -------
706        ```py
707        vec = Vec((2, 1, 3))
708        vec.sort()
709        assert vec == [1, 2, 3]
710        ```
711        """
712        # key can be `None` here just fine, idk why pyright is complaining.
713        self._buf.sort(key=key, reverse=reverse)  # pyright: ignore
714
715    def count(self, item: T) -> int:
716        """Return the number of occurrences of `item` in the vec.
717
718        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
719
720        Example
721        --------
722        ```py
723        vec = Vec((1, 2, 3, 3))
724        assert vec.count(3) == 2
725        ```
726        """
727        return self._buf.count(item)
728
729    # lists are unhashable.
730    __hash__: None = None

A basic implementation of Rust's Vec<T> type, backed by Python list<T>.

See the module top level documentation for more information.

Example
names = Vec[str]()

names.push('foo')
names.push('bar')

print(names) # ['foo', 'bar']
assert names.len() == 2
Implementations

This class implements Vec in Rust.

Vec(iterable: Iterable[~T] | None = None)
206    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
207        if isinstance(iterable, list):
208            # Calling `list()` on another list will copy it, So instead we just point to it.
209            self._buf = iterable
210        elif isinstance(iterable, Vec):
211            self._buf = iterable._buf
212        # any other iterable that ain't a list needs to get copied into a new list.
213        else:
214            self._buf = list(iterable) if iterable else []
215
216        self._capacity: int | None = None

Create a new Vec<T> from an iterable.

Example
vec = Vec([1.2, 3.4])
@classmethod
def with_capacity(cls, capacity: int) -> Vec[~T]:
218    @classmethod
219    def with_capacity(cls, capacity: int) -> Vec[T]:
220        """Create a new `Vec` with at least the specified capacity.
221        This vec will be able to hold `capacity` elements without pushing further.
222
223        The capacity may dynamically grow if `Vec.reserve` is called at runtime.
224
225        Check out `Vec.push_within_capacity` as well.
226
227        Example
228        -------
229        ```py
230        vec = Vec.with_capacity(3)
231        assert vec.len() == 0 and vec.capacity() >= 3
232
233        vec.push(1)
234        vec.push(2)
235        vec.push(3)
236        print(vec.len()) # 3
237
238        # This won't push.
239        vec.push(4)
240        ```
241        """
242        v = cls()
243        v._capacity = capacity
244        return v

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

The capacity may dynamically grow if Vec.reserve is called at runtime.

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_slice(self) -> sain.collections.Slice[~T]:
246    def as_slice(self) -> Slice[T]:
247        """Return an immutable view over this vector elements.
248
249        No copying occurs.
250
251        Example
252        -------
253        ```py
254        def send_bytes(v: Slice[int]) -> None:
255            # access `vec` in a read-only mode.
256            socket.write_all(v)
257
258        buf = Vec([1, 2, 3])
259        send_bytes(buf.as_slice())
260        ```
261        """
262        # NOTE: Although, we could just return `self`, but,
263        # we want to return an exclusive view.
264        return Slice(self._buf)

Return an immutable view over this vector elements.

No copying occurs.

Example
def send_bytes(v: Slice[int]) -> None:
    # access `vec` in a read-only mode.
    socket.write_all(v)

buf = Vec([1, 2, 3])
send_bytes(buf.as_slice())
def as_mut_slice(self) -> sain.collections.SliceMut[~T]:
266    def as_mut_slice(self) -> SliceMut[T]:
267        """Return a mutable view over this vector elements.
268
269        Example
270        -------
271        ```py
272        def read_bytes(buf: SliceMut[int]) -> None:
273            # similar to how `Write::write_to_end` requires a `&mut [u8]`
274            socket.read_to_end(buf)
275
276        buf: Vec[int] = Vec()
277        read_bytes(buf.as_mut_slice()) # or just `buf`
278        assert buf.len() >= 1
279        ```
280        """
281        return self

Return a mutable view over this vector elements.

Example
def read_bytes(buf: SliceMut[int]) -> None:
    # similar to how `Write::write_to_end` requires a `&mut [u8]`
    socket.read_to_end(buf)

buf: Vec[int] = Vec()
read_bytes(buf.as_mut_slice()) # or just `buf`
assert buf.len() >= 1
def capacity(self) -> int:
283    def capacity(self) -> int:
284        """Return the capacity of this vector if set, 0 if not .
285
286        The number `0` here has two different Meanings:
287        - The first means the `Vec` doesn't have a specific capacity set.
288        - The second means the `Vec` literally initialized with `0` capacity.
289
290        Example
291        -------
292        ```py
293        vec_with_cap = Vec.with_capacity(3)
294        assert vec_with_cap.capacity().unwrap() == 3
295
296        vec = Vec([1, 2, 3])
297        assert vec.capacity() == 0
298        ```
299        """
300        return 0 if self._capacity is None else self._capacity

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

The number 0 here has two different Meanings:

  • The first means the Vec doesn't have a specific capacity set.
  • The second means the Vec literally initialized with 0 capacity.
Example
vec_with_cap = Vec.with_capacity(3)
assert vec_with_cap.capacity().unwrap() == 3

vec = Vec([1, 2, 3])
assert vec.capacity() == 0
def leak(self) -> list[~T]:
302    def leak(self) -> list[T]:
303        """Consumes and leaks the Vec, returning a mutable reference to the contents.
304
305        After calling this, this vec will no longer reference the underlying buffer,
306        therefore, it becomes unusable.
307
308        if `self` is uninitialized, an empty list is returned.
309
310        This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without
311        any copies.
312
313        Example
314        -------
315        ```py
316        x = Vec([1, 2, 3])
317        owned = x.leak()
318        # x is now unusable.
319        owned[0] += 1
320        assert owned == [2, 2, 3]
321        ```
322        """
323        # don't point to `_buf` anymore.
324        tmp = self._buf
325        del self._buf
326        return tmp  # pyright: ignore[reportReturnType] - _buf is a list.

Consumes and leaks the Vec, returning a mutable reference to the contents.

After calling this, this vec will no longer reference the underlying buffer, therefore, it becomes unusable.

if self is uninitialized, an empty list is returned.

This function is only useful when you want to detach from a Vec<T> and get a list[T] without any copies.

Example
x = Vec([1, 2, 3])
owned = x.leak()
# x is now unusable.
owned[0] += 1
assert owned == [2, 2, 3]
def split_off(self, at: int) -> Vec[~T]:
328    def split_off(self, at: int) -> Vec[T]:
329        """Split the vector off at the specified position.
330
331        Returns a new vector at the range of `[at : len]`, leaving `self` at `[0: at]`.
332
333        if this vec is empty, `self` is returned unchanged.
334
335        Example
336        -------
337        ```py
338        origin = Vec((1, 2, 3, 4))
339        split = vec.split_off(2)
340
341        print(origin, split)  # [1, 2], [3, 4]
342        ```
343
344        Raises
345        ------
346        `IndexError`
347            This method will raise if `at` > `len(self)`
348        """
349        len_ = self.len()
350        if at > len_:
351            raise IndexError(
352                f"Index `at` ({at}) should be <= than len of vector ({len_}) "
353            ) from None
354
355        # Either the list is empty or uninit.
356        if not self._buf:
357            return self
358
359        split = self._buf[at:len_]
360        del self._buf[at:len_]
361        return Vec(split)

Split the vector off at the specified position.

Returns a new vector at the range of [at : len], leaving self at [0: at].

if this vec is empty, self is returned unchanged.

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

print(origin, split)  # [1, 2], [3, 4]
Raises
  • IndexError: This method will raise if at > len(self)
def truncate(self, size: int) -> None:
363    def truncate(self, size: int) -> None:
364        """Shortens the vec, keeping the first `size` elements and dropping the rest.
365
366        Example
367        -------
368        ```py
369        vec = Vec([1,2,3])
370        vec.truncate(1)
371        assert vec == [1]
372        ```
373        """
374        del self._buf[size:]

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

Example
vec = Vec([1,2,3])
vec.truncate(1)
assert vec == [1]
def retain(self, f: Callable[[~T], bool]) -> None:
376    def retain(self, f: collections.Callable[[T], bool]) -> None:
377        """Retains only the elements specified by the predicate.
378
379        This operation occurs in-place without copying the original list.
380
381        Example
382        -------
383        ```py
384        vec = Vec([1, 2, 3])
385        vec.retain(lambda elem: elem > 1)
386
387        assert vec == [2, 3]
388        ```
389
390        The impl for this method is very simple
391        ```py
392        for idx, e in enumerate(vec):
393            if not f(e):
394                del vec[idx]
395        ```
396        """
397        idx = 0
398        while idx < len(self._buf):
399            if not f(self._buf[idx]):
400                del self._buf[idx]
401            else:
402                idx += 1

Retains only the elements specified by the predicate.

This operation occurs in-place without copying the original list.

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

assert vec == [2, 3]

The impl for this method is very simple

for idx, e in enumerate(vec):
    if not f(e):
        del vec[idx]
def swap_remove(self, item: ~T) -> ~T:
404    def swap_remove(self, item: T) -> T:
405        """Remove the first appearance of `item` from this vector and return it.
406
407        Raises
408        ------
409        `ValueError`
410            if `item` is not in this vector.
411
412        Example
413        -------
414        ```py
415        vec = Vec(('a', 'b', 'c'))
416        element = vec.swap_remove('a')
417        assert vec == ['b', 'c'] and element == 'a'
418        ```
419        """
420        return self._buf.pop(self.index(item))

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

Raises
  • ValueError: if item is not in this vector.
Example
vec = Vec(('a', 'b', 'c'))
element = vec.swap_remove('a')
assert vec == ['b', 'c'] and element == 'a'
def push(self, item: ~T) -> None:
422    def push(self, item: T) -> None:
423        """Push an element at the end of the vector.
424
425        Example
426        -------
427        ```py
428        vec = Vec()
429        vec.push(1)
430
431        assert vec == [1]
432        ```
433        """
434        if (cap := self._capacity) is not None and len(self._buf) == cap:
435            # max cap reached.
436            return
437
438        self._buf.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]':
440    def push_within_capacity(self, x: T) -> Result[None, T]:
441        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
442
443        Example
444        -------
445        ```py
446        vec: Vec[int] = Vec.with_capacity(3)
447        for i in range(3):
448            match vec.push_within_capacity(i):
449                case Ok(_):
450                    print("All good.")
451                case Err(elem):
452                    print("Reached max cap :< can't push", elem)
453        ```
454
455        Or you can also just call `Vec.push` and it will push if there's is sufficient capacity.
456        ```py
457        vec: Vec[int] = Vec.with_capacity(3)
458
459        vec.extend((1, 2, 3))
460        vec.push(4)
461
462        assert vec.len() == 3
463        ```
464        """
465        if self.len() == self._capacity:
466            return _result.Err(x)
467
468        self._buf.append(x)
469        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 :< can't push", elem)

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

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

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

assert vec.len() == 3
def reserve(self, additional: int) -> None:
471    def reserve(self, additional: int) -> None:
472        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
473
474        Example
475        -------
476        ```py
477        vec = Vec.with_capacity(3)
478
479        for i in range(3):
480            vec.push(i)
481
482        match vec.push_within_capacity(4):
483            case Ok(_):
484                print("Pushed 4 successfully.")
485            case Err(elem):
486                vec.reserve(1)  # Reserve capacity for 1 more element.
487                vec.push(4)  # Now we can push 4.
488        ```
489        """
490        if self._capacity is not None:
491            self._capacity += additional

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

Example
vec = Vec.with_capacity(3)

for i in range(3):
    vec.push(i)

match vec.push_within_capacity(4):
    case Ok(_):
        print("Pushed 4 successfully.")
    case Err(elem):
        vec.reserve(1)  # Reserve capacity for 1 more element.
        vec.push(4)  # Now we can push 4.
def shrink_to_fit(self) -> None:
493    def shrink_to_fit(self) -> None:
494        """Shrink the capacity of this `Vec` to match its length.
495
496        If `self` is initialized with no capacity, This is a `NOP`.
497
498        Example
499        -------
500        ```py
501        s = Vec([1, 2, 3, 4])
502
503        s.reserve(100)
504        s.shrink_to_fit()
505        assert s.capacity() == 4
506        ```
507        """
508        if self._capacity is None:
509            return
510
511        # The capacity is never less than the length.
512        self._capacity = min(self.__len__(), self._capacity)

Shrink the capacity of this Vec to match its length.

If self is initialized with no capacity, This is a NOP.

Example
s = Vec([1, 2, 3, 4])

s.reserve(100)
s.shrink_to_fit()
assert s.capacity() == 4
def shrink_to(self, min_capacity: int) -> None:
514    def shrink_to(self, min_capacity: int) -> None:
515        """Shrink the capacity of this `Vec` to a minimum specified capacity.
516
517        If `self` is initialized with no capacity or the current capacity is less than the lower limit,
518        This is a `NOP`.
519
520        Example
521        -------
522        ```py
523        vec = Vec.with_capacity(10)
524        vec.extend([1, 2, 3])
525        assert vec.capacity() >= 10
526        vec.shrink_to(4)
527        assert vec.capacity() >= 4
528        vec.shrink_to(0)
529        ```
530        """
531        if self._capacity is None or self._capacity <= min_capacity:
532            return
533
534        # Ensure the capacity is not reduced below the current length of the vector.
535        self._capacity = max(self.__len__(), min_capacity)

Shrink the capacity of this Vec to a minimum specified capacity.

If self is initialized with no capacity or the current capacity is less than the lower limit, This is a NOP.

Example
vec = Vec.with_capacity(10)
vec.extend([1, 2, 3])
assert vec.capacity() >= 10
vec.shrink_to(4)
assert vec.capacity() >= 4
vec.shrink_to(0)
def append(self, value: ~T) -> None:
541    def append(self, value: T) -> None:
542        """Append an element at the end of the vector.
543
544        An alias to `Vec.push`.
545        """
546        self._buf.append(value)

Append an element at the end of the vector.

An alias to Vec.push.

def insert(self, index: int, value: ~T) -> None:
548    def insert(self, index: int, value: T) -> None:
549        """Insert an element at the position `index`.
550
551        Example
552        --------
553        ```py
554        vec = Vec((2, 3))
555        vec.insert(0, 1)
556        assert vec == [1, 2, 3]
557        ```
558        """
559        self[index] = value

Insert an element at the position index.

Example
vec = Vec((2, 3))
vec.insert(0, 1)
assert vec == [1, 2, 3]
def pop(self, index: int = -1) -> '_option.Option[T]':
561    def pop(self, index: int = -1) -> _option.Option[T]:
562        """Removes the last element from the vector and returns it, or `None` if it is empty.
563
564        Example
565        -------
566        ```py
567        vec = Vec((1, 2, 3))
568        assert vec.pop() == Some(3)
569        assert vec == [1, 2]
570        ```
571        """
572        if not self._buf:
573            return _option.NOTHING
574
575        return _option.Some(self._buf.pop(index))

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

Example
vec = Vec((1, 2, 3))
assert vec.pop() == Some(3)
assert vec == [1, 2]
def pop_if(self, pred: Callable[[~T], bool]) -> '_option.Option[T]':
577    def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]:
578        """Removes the last element from a vector and returns it if `f` returns `True`,
579        or `None` if it is empty.
580
581        Example
582        -------
583        ```py
584        vec = Vec((1, 2, 3))
585        assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
586        assert vec == [1, 2]
587        ```
588        """
589        if not self._buf:
590            return _option.NOTHING
591
592        if pred(self[-1]):
593            return self.pop()
594
595        return _option.NOTHING

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

Example
vec = Vec((1, 2, 3))
assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
assert vec == [1, 2]
def dedup(self) -> None:
597    def dedup(self) -> None:
598        """Removes consecutive repeated elements in the vector according to their `__eq__`
599        implementation.
600
601        Example
602        -------
603        ```py
604        vec = Vec([1, 2, 2, 3, 2])
605        vec.dedup()
606        assert vec == [1, 2, 3, 2]
607        """
608        self.dedup_by(lambda a, b: a == b)

Removes consecutive repeated elements in the vector according to their __eq__ implementation.

Example

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

def dedup_by(self, same_bucket: Callable[[~T, ~T], bool]) -> None:
610    def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None:
611        """Removes all but the first of consecutive elements in the vector satisfying a given equality.
612
613        Example
614        -------
615        ```py
616        vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
617        vec.dedup_by(lambda a, b: a.lower() == b.lower())
618        assert vec == ["foo", "bar", "baz", "bar"]
619        ```
620
621        Only remove consecutive duplicates.
622        ```py
623        vec = Vec([1, 2, 2, 3, 4, 1])
624        vec.dedup_by(lambda a, b: a == b)
625        # The final 1 is not adjacent to the first 1, so it is not removed.
626        assert vec == [1, 2, 3, 4, 1]
627        ```
628        """
629
630        if not self._buf or (len_ := len(self._buf)) <= 1:
631            return
632
633        idx = 1
634        while idx < len_:
635            if same_bucket(self._buf[idx], self._buf[idx - 1]):
636                del self._buf[idx]
637                len_ -= 1
638            else:
639                idx += 1

Removes all but the first of consecutive elements in the vector satisfying a given equality.

Example
vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
vec.dedup_by(lambda a, b: a.lower() == b.lower())
assert vec == ["foo", "bar", "baz", "bar"]

Only remove consecutive duplicates.

vec = Vec([1, 2, 2, 3, 4, 1])
vec.dedup_by(lambda a, b: a == b)
# The final 1 is not adjacent to the first 1, so it is not removed.
assert vec == [1, 2, 3, 4, 1]
def remove(self, item: ~T) -> None:
641    def remove(self, item: T) -> None:
642        """Remove the first appearance of `item` from this vector.
643
644        Example
645        -------
646        ```py
647        vec = Vec(('a', 'b', 'c'))
648        vec.remove('a')
649        assert vec == ['b', 'c']
650        ```
651        """
652        self._buf.remove(item)

Remove the first appearance of item from this vector.

Example
vec = Vec(('a', 'b', 'c'))
vec.remove('a')
assert vec == ['b', 'c']
def extend(self, iterable: Iterable[~T]) -> None:
654    def extend(self, iterable: collections.Iterable[T]) -> None:
655        """Extend this vector from another iterable.
656
657        Example
658        -------
659        ```py
660        vec = Vec((1, 2, 3))
661        vec.extend((4, 5, 6))
662
663        assert vec == [1, 2, 3, 4, 5, 6]
664        ```
665        """
666        self._buf.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]:
668    def copy(self) -> Vec[T]:
669        """Copy all elements of `self` into a new vector.
670
671        Example
672        -------
673        ```py
674        original = Vec((1,2,3))
675        copy = original.copy()
676        copy.push(4)
677
678        print(original) # [1, 2, 3]
679        ```
680        """
681        return Vec(self._buf[:])

Copy all elements of self into a new vector.

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

print(original) # [1, 2, 3]
def clear(self) -> None:
683    def clear(self) -> None:
684        """Clear all elements of this vector.
685
686        Example
687        -------
688        ```py
689        vec = Vec((1,2,3))
690        vec.clear()
691        assert vec.len() == 0
692        ```
693        """
694        self._buf.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:
696    def sort(
697        self,
698        *,
699        key: collections.Callable[[T], SupportsRichComparison] | None = None,
700        reverse: bool = False,
701    ) -> None:
702        """This method sorts the list in place, using only < comparisons between items.
703
704        Example
705        -------
706        ```py
707        vec = Vec((2, 1, 3))
708        vec.sort()
709        assert vec == [1, 2, 3]
710        ```
711        """
712        # key can be `None` here just fine, idk why pyright is complaining.
713        self._buf.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 count(self, item: ~T) -> int:
715    def count(self, item: T) -> int:
716        """Return the number of occurrences of `item` in the vec.
717
718        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
719
720        Example
721        --------
722        ```py
723        vec = Vec((1, 2, 3, 3))
724        assert vec.count(3) == 2
725        ```
726        """
727        return self._buf.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
@rustc_diagnostic_item('Error')
@typing.runtime_checkable
class Error(sain.ToString, typing.Protocol):
 68@rustc_diagnostic_item("Error")
 69@typing.runtime_checkable
 70class Error(ToString, typing.Protocol):
 71    """`Error` is an interface usually used for values that returns `sain.Result[T, E]`
 72
 73    where `E` is an implementation of this interface.
 74
 75    Example
 76    -------
 77    ```py
 78    import requests
 79    import http
 80    from dataclasses import dataclass
 81
 82    from sain import Error
 83    from sain import Result, Ok, Err
 84
 85    # an http error.
 86    @dataclass
 87    class HTTPError(Error):
 88        response: requests.Response
 89        kind: http.HTTPStatus
 90        message: str = ""
 91
 92        def description(self) -> str:
 93            return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}"
 94
 95    # A simple request that handles [404] responses.
 96    def request(url: str, uid: int) -> Result[requests.Response, HTTPError]:
 97        response = requests.get(url, json={"user_id": uid})
 98        if response.status_code == 404:
 99            return Err(
100                HTTPError(
101                    response,
102                    kind=http.HTTPStatus.NOT_FOUND,
103                    message=f"Resource not found for user_id {uid}",
104                )
105            )
106        return Ok(response)
107
108    # Execute the request
109    match request("some-url.com", 0):
110        case Ok(response):
111            # Deal with the response
112            ...
113        case Err(why):
114            # Deal with the error.
115            print(why)
116
117    ```
118    """
119
120    __slots__ = ("message",)
121
122    def __init__(self, message: str = "") -> None:
123        self.message = message
124        """A basic error message."""
125
126    def source(self) -> Option[type[Error]]:
127        """The source of this error, if any."""
128        return _option.NOTHING
129
130    def description(self) -> str:
131        """Context for this error."""
132        return ""
133
134    def to_string(self) -> str:
135        return self.__repr__()
136
137    def __repr__(self) -> str:
138        source = None if (src := self.source()).is_none() else src
139        return (
140            f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})'
141        )
142
143    def __str__(self) -> str:
144        return self.message
145
146    # An error is always falsy.
147    def __bool__(self) -> typing.Literal[False]:
148        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
import http
from dataclasses import dataclass

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

# an http error.
@dataclass
class HTTPError(Error):
    response: requests.Response
    kind: http.HTTPStatus
    message: str = ""

    def description(self) -> str:
        return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}"

# A simple request that handles [404] responses.
def request(url: str, uid: int) -> Result[requests.Response, HTTPError]:
    response = requests.get(url, json={"user_id": uid})
    if response.status_code == 404:
        return Err(
            HTTPError(
                response,
                kind=http.HTTPStatus.NOT_FOUND,
                message=f"Resource not found for user_id {uid}",
            )
        )
    return Ok(response)

# Execute the request
match request("some-url.com", 0):
    case Ok(response):
        # Deal with the response
        ...
    case Err(why):
        # Deal with the error.
        print(why)
Implementations

This class implements Error in Rust.

Error(message: str = '')
122    def __init__(self, message: str = "") -> None:
123        self.message = message
124        """A basic error message."""
message

A basic error message.

def source(self) -> 'Option[type[Error]]':
126    def source(self) -> Option[type[Error]]:
127        """The source of this error, if any."""
128        return _option.NOTHING

The source of this error, if any.

def description(self) -> str:
130    def description(self) -> str:
131        """Context for this error."""
132        return ""

Context for this error.

def to_string(self) -> str:
134    def to_string(self) -> str:
135        return self.__repr__()

Converts the given value to a str.

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

Returns True if the value has expired.

def on_expire(self, callback: Callable[[+T], typing.Any]) -> Self:
109    def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self:
110        """Set a callback that will be invoked when this value gets expired.
111
112        Both async and sync callbacks are supported.
113
114        Example
115        -------
116        ```py
117        async def sink(message: str) -> None:
118            await client.create_message(message)
119            print("Sinked", message)
120
121        box = Box("bluh", 5).on_expire(sink)
122
123        while box.get().is_some():
124            time.sleep(5)
125        ```
126        First `.get` call on an expired box, the `sink` callback will be invoked,
127        also the inner value will be set to `Some(None)`.
128
129        After 5 seconds.
130        ```py
131        assert box.get() == Some("bluh") # This last call invokes the callback.
132        # Sinked bluh
133        assert box.get().is_none()
134        ```
135        """
136        self._on_expire = callback
137        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:
139    def remaining(self) -> float:
140        """Returns when this box will expire in seconds.
141
142        Example
143        --------
144        ```py
145        jogo = Box("jogo", 3)
146        assert jogo.get().unwrap() == "jogo"
147
148        time.sleep(1)
149        assert jogo.remaining() == 2
150        ```
151        """
152        if not self._mono:
153            return 0.0
154
155        return math.floor(
156            (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99
157        )

Returns when this box will expire in seconds.

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

time.sleep(1)
assert jogo.remaining() == 2
def get(self) -> 'Option[T]':
159    def get(self) -> Option[T]:
160        """Get the contained value if it was not expired, otherwise `Some(None)` is returned.
161
162        Example
163        -------
164        ```py
165        pizza = Box("pizza", timedelta(days=1))
166
167        while not pizza.get().is_none():
168            # Do stuff with the value while its not expired.
169
170        # After 1 day.
171        assert pizza.get().is_none()
172        ```
173        """
174        if self.has_expired:
175            if self._on_expire is not None:
176                with warnings.catch_warnings():
177                    # ignore the warnings from `unwrap_unchecked`.
178                    warnings.simplefilter("ignore", category=ub_checks)
179                    try:
180                        if asyncio.iscoroutinefunction(self._on_expire):
181                            futures.loop().run_until_complete(
182                                self._on_expire(self._inner.unwrap_unchecked())
183                            )
184                        else:
185                            self._on_expire(self._inner.unwrap_unchecked())
186                    finally:
187                        self._on_expire = None
188
189            self._inner = option.NOTHING
190            self._mono = None
191            # SAFETY: The value is expired, therefore we always return None.
192            return option.NOTHING
193
194        if self._mono is None:
195            self._mono = time.monotonic()
196
197        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()
@rustc_diagnostic_item('From')
@typing.runtime_checkable
class From(typing.Protocol[-T_co]):
148@rustc_diagnostic_item("From")
149@typing.runtime_checkable
150class From(typing.Protocol[T_co]):
151    """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
152
153    As the Rust documentation says, One should always prefer implementing From over Into
154    because implementing From automatically provides one with an implementation of Into.
155
156    But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types
157    that impl `From<T>`.
158
159    So for the sake of simplicity, You should implement whichever interface you want deal with,
160    Or simply, implement both as the same time.
161
162    Example
163    -------
164    ```py
165    @dataclass
166    class Id(From[str]):
167        value: int
168
169        @classmethod
170        def from_value(cls, value: str) -> Self:
171            return cls(value=int(value))
172
173    ```
174    """
175
176    __slots__ = ()
177
178    @classmethod
179    def from_value(cls, value: T_co) -> Self:
180        """Perform the conversion."""
181        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_value(cls, value: str) -> Self:
        return cls(value=int(value))
Implementations

This class implements From in Rust.

From(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
@classmethod
def from_value(cls, value: -T_co) -> Self:
178    @classmethod
179    def from_value(cls, value: T_co) -> Self:
180        """Perform the conversion."""
181        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryFrom')
@typing.runtime_checkable
class TryFrom(typing.Protocol[-T_co, ~E]):
184@rustc_diagnostic_item("TryFrom")
185@typing.runtime_checkable
186class TryFrom(typing.Protocol[T_co, E]):
187    """Simple and safe type conversions that may fail in a controlled way under some circumstances.
188    It is the reciprocal of `TryInto`.
189
190    It is useful to implement this when you know that the conversion may fail in some way.
191
192    Generic Implementations
193    -------------------
194    This interface takes two type arguments, and return `Result<Self, E>`
195
196    * `T`: Which's the first generic `T` is the type that's being converted from.
197    * `E`: If the conversion fails in a way, this is what will return as the error.
198    * `Self`: Which's the instance of the class that is being converted into.
199
200    Example
201    -------
202    ```py
203    @dataclass
204    class Id(TryFrom[str, str]):
205        value: int
206
207        @classmethod
208        def try_from(cls, value: str) -> Result[Self, str]:
209            if not value.isnumeric():
210                # NaN
211                return Err(f"Couldn't convert: {value} to self")
212            # otherwise convert it to an Id instance.
213            return Ok(value=cls(int(value)))
214    ```
215    """
216
217    __slots__ = ()
218
219    @classmethod
220    def try_from(cls, value: T_co) -> Result[Self, E]:
221        """Perform the conversion."""
222        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)))
Implementations

This class implements TryFrom in Rust.

TryFrom(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
@classmethod
def try_from(cls, value: -T_co) -> 'Result[Self, E]':
219    @classmethod
220    def try_from(cls, value: T_co) -> Result[Self, E]:
221        """Perform the conversion."""
222        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryFrom')
@typing.runtime_checkable
class Into(typing.Protocol[+T_cov]):
225@rustc_diagnostic_item("TryFrom")
226@typing.runtime_checkable
227class Into(typing.Protocol[T_cov]):
228    """Conversion from `self`, which may or may not be expensive.
229
230    Example
231    -------
232    ```py
233    @dataclass
234    class Id(Into[str]):
235        value: int
236
237        def into(self) -> str:
238            return str(self.value)
239    ```
240    """
241
242    __slots__ = ()
243
244    def into(self) -> T_cov:
245        """Perform the conversion."""
246        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)
Implementations

This class implements TryFrom in Rust.

Into(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
def into(self) -> +T_cov:
244    def into(self) -> T_cov:
245        """Perform the conversion."""
246        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('TryInto')
@typing.runtime_checkable
class TryInto(typing.Protocol[~T, ~E]):
249@rustc_diagnostic_item("TryInto")
250@typing.runtime_checkable
251class TryInto(typing.Protocol[T, E]):
252    """An attempted conversion from `self`, which may or may not be expensive.
253
254    It is useful to implement this when you know that the conversion may fail in some way.
255
256    Generic Implementations
257    -------------------
258    This interface takes two type arguments, and return `Result<T, E>`
259
260    * `T`: The first generic `T` is the type that's being converted into.
261    * `E`: If the conversion fails in a way, this is what will return as the error.
262
263    Example
264    -------
265    ```py
266    @dataclass
267    class Id(TryInto[int, str]):
268        value: str
269
270        def try_into(self) -> Result[int, str]:
271            if not self.value.isnumeric():
272                return Err(f"{self.value} is not a number...")
273            return Ok(int(self.value))
274    ```
275    """
276
277    __slots__ = ()
278
279    def try_into(self) -> Result[T, E]:
280        """Perform the conversion."""
281        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))
Implementations

This class implements TryInto in Rust.

TryInto(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
def try_into(self) -> 'Result[T, E]':
279    def try_into(self) -> Result[T, E]:
280        """Perform the conversion."""
281        raise NotImplementedError

Perform the conversion.

@rustc_diagnostic_item('ToString')
@typing.runtime_checkable
class ToString(typing.Protocol):
284@rustc_diagnostic_item("ToString")
285@typing.runtime_checkable
286class ToString(typing.Protocol):
287    """A trait for explicitly converting a value to a `str`.
288
289    Example
290    -------
291    ```py
292    class Value[T: bytes](ToString):
293        buffer: T
294
295        def to_string(self) -> str:
296            return self.buffer.decode("utf-8")
297    ```
298    """
299
300    __slots__ = ()
301
302    def to_string(self) -> str:
303        """Converts the given value to a `str`.
304
305        Example
306        --------
307        ```py
308        i = 5  # assume `int` implements `ToString`
309        five = "5"
310        assert five == i.to_string()
311        ```
312        """
313        raise NotImplementedError
314
315    def __str__(self) -> str:
316        return self.to_string()

A trait for explicitly converting a value to a str.

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

    def to_string(self) -> str:
        return self.buffer.decode("utf-8")
Implementations

This class implements ToString in Rust.

ToString(*args, **kwargs)
1945def _no_init_or_replace_init(self, *args, **kwargs):
1946    cls = type(self)
1947
1948    if cls._is_protocol:
1949        raise TypeError('Protocols cannot be instantiated')
1950
1951    # Already using a custom `__init__`. No need to calculate correct
1952    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1953    if cls.__init__ is not _no_init_or_replace_init:
1954        return
1955
1956    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1957    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1958    # searches for a proper new `__init__` in the MRO. The new `__init__`
1959    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1960    # instantiation of the protocol subclass will thus use the new
1961    # `__init__` and no longer call `_no_init_or_replace_init`.
1962    for base in cls.__mro__:
1963        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1964        if init is not _no_init_or_replace_init:
1965            cls.__init__ = init
1966            break
1967    else:
1968        # should not happen
1969        cls.__init__ = object.__init__
1970
1971    cls.__init__(self, *args, **kwargs)
def to_string(self) -> str:
302    def to_string(self) -> str:
303        """Converts the given value to a `str`.
304
305        Example
306        --------
307        ```py
308        i = 5  # assume `int` implements `ToString`
309        five = "5"
310        assert five == i.to_string()
311        ```
312        """
313        raise NotImplementedError

Converts the given value to a str.

Example
i = 5  # assume `int` implements `ToString`
five = "5"
assert five == i.to_string()
__version__ = '1.4.0'
__url__ = 'https://github.com/nxtlo/sain'
__author__ = 'nxtlo'
__about__ = 'Sain is a dependency-free library that implements some of the Rust coretypes which abstracts over common patterns in Rust for Python.'
__license__ = 'BSD 3-Clause License'