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:

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

An abstract interface for dealing with iterators.

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

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

If you want to use a ready iterator for general purposes, Use Iter. This interface is only for 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]:
176    @staticmethod
177    @typing.final
178    def default() -> Empty[Item]:
179        """Return the default iterator for this type. It returns an empty iterator.
180
181        Example
182        -------
183        ```py
184        it: Iterator[int] = Iter.default()
185        assert t.next().is_none()
186        ```
187        """
188        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]:
198    @typing.final
199    def collect(
200        self, *, cast: collections.Callable[[Item], OtherItem] | None = None
201    ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]:
202        """Collects all items in the iterator into a sequence.
203
204        Example
205        -------
206        ```py
207        iterator = Iter(range(3))
208        iterator.collect()
209        # [0, 1, 2, 3]
210        iterator.collect(cast=str) # Map each element and collect it.
211        # ['0', '1', '2', '3']
212        ```
213
214        Parameters
215        ----------
216        cast: `T | None`
217            An optional type to cast the items into.
218            If not provided the items will be returned as it's original type.
219        """
220        if cast is not None:
221            return [cast(i) for i in self]
222
223        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:
225    @typing.final
226    def collect_into(self, collection: Collector[Item]) -> None:
227        """Consume this iterator, extending all items in the iterator into a mutable `collection`.
228
229        Example
230        -------
231        ```py
232        iterator = Iter([1, 1, 2, 3, 4, 2, 6])
233        uniques = set()
234        iterator.collect_into(uniques)
235        # assert uniques == {1, 2, 3, 4, 6}
236        ```
237
238        Parameters
239        ----------
240        collection: `MutableSequence[T]` | `set[T]`
241            The collection to extend the items in this iterator with.
242        """
243        if isinstance(collection, collections.MutableSequence):
244            collection.extend(_ for _ in self)
245        elif isinstance(collection, collections.MutableSet):
246            collection.update(_ for _ in self)
247        else:
248            for idx, item in enumerate(self):
249                collection[idx] = item

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

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

Convert this iterator into Vec[T].

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

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

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

Example
it = Iter((1, 2, 3))
it.sink()
assert it.next().is_none()
@typing.final
def raw_parts(self) -> Generator[~Item, None, None]:
283    @typing.final
284    def raw_parts(self) -> collections.Generator[Item, None, None]:
285        """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items.
286
287        ```py
288        it = Iter("cba")
289        sort = sorted(it.raw_parts())
290
291        assert it.count() == 0
292        assert sort == ["a", "b", "c"]
293        ```
294        """
295        for item in self:
296            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]':
302    def next(self) -> Option[Item]:
303        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
304
305        Example
306        -------
307        ```py
308        iterator = Iter(["1", "2"])
309        assert iterator.next() == Some("1")
310        assert iterator.next() == Some("2")
311        assert iterator.next().is_none()
312        ```
313        """
314        try:
315            return _option.Some(self.__next__())
316        except StopIteration:
317            # ! SAFETY: No more items in the iterator.
318            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]:
320    def cloned(self) -> Cloned[Item]:
321        """Creates an iterator which shallow copies its elements by reference.
322
323        .. note::
324            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
325            on each item that is being yielded.
326
327        Example
328        -------
329        ```py
330        @dataclass
331        class User:
332            users_ids: list[int] = []
333
334        # An iterator which elements points to the same user.
335        user = User()
336        it = Iter((user, user))
337
338        for u in it.cloned():
339            u.user_ids.append(1)
340
341        # We iterated over the same user pointer twice and appended "1"
342        # since `copy` returns a shallow copy of nested structures.
343        assert len(user.user_ids) == 2
344        ```
345        """
346        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]:
348    def copied(self) -> Copied[Item]:
349        """Creates an iterator which copies all of its elements by value.
350
351        If you only need a copy of the item reference, Use `.cloned()` instead.
352
353        .. note::
354            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
355            on each item that is being yielded.
356
357        Example
358        -------
359        ```py
360        @dataclass
361        class User:
362            users_ids: list[int] = []
363
364        # An iterator which elements points to the same user.
365        user = User()
366        it = Iter((user, user))
367
368        for u in it.copied():
369            # A new list is created for each item.
370            u.user_ids.append(1)
371
372        # The actual list is untouched since we consumed a deep copy of it.
373        assert len(user.user_ids) == 0
374        ```
375        """
376        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]:
378    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
379        """Maps each item in the iterator to another type.
380
381        Example
382        -------
383        ```py
384        iterator = Iter(["1", "2", "3"]).map(int)
385
386        for item in iterator:
387            assert isinstance(item, int)
388        ```
389
390        Parameters
391        ----------
392        predicate: `Callable[[Item], OtherItem]`
393            The function to map each item in the iterator to the other type.
394        """
395        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]:
397    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
398        """Filters the iterator to only yield items that match the predicate.
399
400        Example
401        -------
402        ```py
403        places = Iter(['London', 'Paris', 'Los Angeles'])
404        for place in places.filter(lambda place: place.startswith('L')):
405            print(place)
406
407        # London
408        # Los Angeles
409        ```
410        """
411        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]:
413    def take(self, count: int) -> Take[Item]:
414        """Take the first number of items until the number of items
415        are yielded or the end of the iterator is exhausted.
416
417        Example
418        -------
419        ```py
420        iterator = Iter(['c', 'x', 'y'])
421
422        for x in iterator.take(2):
423            assert x in ('c', 'x')
424
425        # <Iter(['c', 'x'])>
426        ```
427        """
428        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]:
430    def skip(self, count: int) -> Skip[Item]:
431        """Skips the first number of items in the iterator.
432
433        Example
434        -------
435        ```py
436        iterator = Iter((1, 2, 3, 4))
437        for i in iterator.skip(2):
438            print(i)
439
440        # 3
441        # 4
442        ```
443        """
444        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]:
446    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
447        """Create a new iterator that yields a tuple of the index and item.
448
449        Example
450        -------
451        ```py
452        iterator = Iter([1, 2, 3])
453        for index, item in iterator.enumerate():
454            print(index, item)
455
456        # 0 1
457        # 1 2
458        # 2 3
459        ```
460        """
461        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]:
463    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
464        """yields items from the iterator while predicate returns `True`.
465
466        The rest of the items are discarded as soon as the predicate returns `False`
467
468        Example
469        -------
470        ```py
471        iterator = Iter(['a', 'ab', 'xd', 'ba'])
472        for x in iterator.take_while(lambda x: 'a' in x):
473            print(x)
474
475        # a
476        # ab
477        ```
478
479        Parameters
480        ----------
481        predicate: `collections.Callable[[Item], bool]`
482            The function to predicate each item in the iterator.
483        """
484        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]:
486    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
487        """Yields items from the iterator while predicate returns `False`.
488
489        Example
490        -------
491        ```py
492        iterator = Iter(['a', 'ab', 'xd', 'ba'])
493        for x in iterator.drop_while(lambda x: 'a' in x):
494            print(x)
495
496        # xd
497        # ba
498        ```
499
500        Parameters
501        ----------
502        predicate: `collections.Callable[[Item], bool]`
503            The function to predicate each item in the iterator.
504        """
505        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]:
507    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
508        """Returns an iterator over `chunk_size` elements of the iterator at a time,
509        starting at the beginning of the iterator.
510
511        Example
512        -------
513        ```py
514        iter = Iter(['a', 'b', 'c', 'd', 'e'])
515        chunks = iter.chunks()
516        assert chunks.next().unwrap() == ['a', 'b']
517        assert chunks.next().unwrap() == ['c', 'd']
518        assert chunks.next().unwrap() == ['e']
519        assert chunks.next().is_none()
520        ```
521        """
522        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:
524    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
525        """Return `True` if all items in the iterator match the predicate.
526
527        Example
528        -------
529        ```py
530        iterator = Iter([1, 2, 3])
531        if iterator.all(lambda item: isinstance(item, int)):
532            print("yes")
533        ```
534
535        Parameters
536        ----------
537        predicate: `collections.Callable[[Item], bool]`
538            The function to test each item in the iterator.
539        """
540        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:
542    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
543        """`True` if any items in the iterator match the predicate.
544
545        Example
546        -------
547        ```py
548        iterator = Iter([1, 2, 3])
549        if iterator.any(lambda item: isinstance(item, int)):
550            print("At least one item is an int.")
551        # At least one item is an int.
552        ```
553
554        Parameters
555        ----------
556        predicate: `collections.Callable[[Item], bool]`
557            The function to test each item in the iterator.
558        """
559        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]]:
561    def zip(
562        self, other: collections.Iterable[OtherItem]
563    ) -> Iter[tuple[Item, OtherItem]]:
564        """Zips the iterator with another iterable.
565
566        Example
567        -------
568        ```py
569        xs = Iter([1, 2, 3])
570        ys = [4, 5, 6]
571
572        iter = xs.zip(ys)
573
574        assert iter.next().unwrap() == (1, 4)
575        assert iter.next().unwrap() == (2, 5)
576        assert iter.next().unwrap() == (3, 6)
577        ```
578
579        Parameters
580        ----------
581        other: `Iterable[OtherItem]`
582            The iterable to zip with.
583        """
584        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]', reverse: bool = False) -> MutableSequence[~Item]:
586    def sort(
587        self,
588        *,
589        key: collections.Callable[[Item], SupportsRichComparison],
590        reverse: bool = False,
591    ) -> collections.MutableSequence[Item]:
592        """Sorts the iterator elements and return it in a mutable sequence.
593
594        Example
595        -------
596        ```py
597        iterator = Iter([3, 1, 6, 7])
598        for item in iterator.sort(key=lambda item: item < 3):
599            print(item)
600        # 1
601        # 3
602        # 6
603        # 7
604        ```
605
606        Parameters
607        ----------
608        key: `collections.Callable[[Item], Any]`
609            The function to sort by.
610        reverse: `bool`
611            Whether to reverse the sort.
612        """
613        return sorted([_ for _ in self], key=key, reverse=reverse)

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]:
615    def reversed(self) -> Iter[Item]:
616        """Returns a new iterator that yields the items in the iterator in reverse order.
617
618        This consumes this iterator into a sequence and return a new iterator containing all of the elements
619        in reversed order.
620
621        Example
622        -------
623        ```py
624        iterator = Iter([3, 1, 6, 7])
625        for item in iterator.reversed():
626            print(item)
627        # 7
628        # 6
629        # 1
630        # 3
631        ```
632        """
633        # NOTE: In order to reverse the iterator we need to
634        # first collect it into some collection.
635        # FIXME: specialize an impl for this.
636        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]:
638    def union(self, other: collections.Iterable[Item]) -> Iter[Item]:
639        """Returns a new iterator that yields all items from both iterators.
640
641        Example
642        -------
643        ```py
644        iterator = Iter([1, 2, 3])
645        other = [4, 5, 6]
646
647        for item in iterator.union(other):
648            print(item)
649        # 1
650        # 2
651        # 3
652        # 4
653        # 5
654        # 6
655        ```
656
657        Parameters
658        ----------
659        other: `Iter[Item]`
660            The iterable to union with.
661        """
662        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]':
664    def first(self) -> Option[Item]:
665        """Returns the first item in the iterator.
666
667        Example
668        -------
669        ```py
670        iterator = Iter([3, 1, 6, 7])
671        iterator.first().is_some_and(lambda x: x == 3)
672        ```
673        """
674        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]':
676    def last(self) -> Option[Item]:
677        """Returns the last item in the iterator.
678
679        Example
680        -------
681        ```py
682        iterator = Iter([3, 1, 6, 7])
683        iterator.last().is_some_and(lambda x: x == 7)
684        ```
685        """
686        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:
688    def count(self) -> int:
689        """Consume this iterator, returning the count of elements it has.
690
691        Example
692        -------
693        ```py
694        it = Iter(range(3))
695        assert it.count() == 3
696        ```
697        """
698        count = 0
699        for _ in self:
700            count += 1
701
702        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]':
704    def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]:
705        """Searches for an element of an iterator that satisfies a predicate.
706
707        If you want the position of the element, use `Iterator.position` instead.
708
709        `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
710        and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None.
711
712        Example
713        -------
714        ```py
715        it = Iter(range(10))
716        item = it.find(lambda num: num > 5)
717        print(item) # 6
718        ```
719        """
720        for item in self:
721            if predicate(item):
722                return _option.Some(item)
723
724        # no more items
725        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]':
727    def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]:
728        """Searches for the position of an element in the iterator that satisfies a predicate.
729
730        If you want the object itself, use `Iterator.find` instead.
731
732        `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator,
733        and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None.
734
735        Example
736        -------
737        ```py
738        it = Iter(range(10))
739        position = it.find(lambda num: num > 5)
740        assert position.unwrap() == 6
741        ```
742        """
743        for position, value in self.enumerate():
744            if predicate(value):
745                return _option.Some(position)
746
747        # no more items
748        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:
750    def fold(
751        self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem]
752    ) -> OtherItem:
753        """Folds every element into an accumulator by applying an operation, returning the final result.
754
755        fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element.
756        The closure returns the value that the accumulator should have for the next iteration.
757
758        The initial value is the value the accumulator will have on the first call.
759
760        After applying this closure to every element of the iterator, fold() returns the accumulator.
761
762        This operation is sometimes called ‘reduce’ or ‘inject’.
763
764        Example
765        -------
766        ```py
767        a = Iter([1, 2, 3, 4])
768        sum = a.fold(0, lambda acc, elem: acc + elem)
769        assert sum == 10
770        ```
771        """
772        accum = init
773        while True:
774            try:
775                x = self.__next__()
776                accum = f(accum, x)
777            except StopIteration:
778                break
779
780        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]':
782    def advance_by(self, n: int) -> _result.Result[None, int]:
783        """Advances the iterator by `n` elements.
784
785        Returns `Result[None, int]`, where `Ok(None)` means the iterator
786        advanced successfully, and `Err(int)` if `None` encountered, where `int`
787        represents the remaining number of steps that could not be advanced because the iterator ran out.
788
789        Example
790        -------
791        ```py
792        it = into_iter([1, 2, 3, 4])
793        assert it.advance_by(2).is_ok()
794        assert it.next() == Some(3)
795        assert it.advance_by(0).is_ok()
796        assert it.advance_by(100) == Err(99)
797        ```
798        """
799        for i in range(n):
800            try:
801                self.__next__()
802            except StopIteration:
803                return _result.Err(n - i)
804
805        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]':
807    def nth(self, n: int) -> Option[Item]:
808        """Returns the `n`th element of the iterator
809
810        Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first
811        value.
812
813        Note all elements before `n` will be skipped / consumed.
814
815        Example
816        -------
817        ```py
818        a = into_iter([1, 2, 3])
819        assert a.iter().nth(1) == Some(2)
820        ```
821        """
822        for _ in range(n):
823            try:
824                self.__next__()
825            except StopIteration:
826                return _option.NOTHING
827
828        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:
830    def sum(self: Sum) -> int:
831        """Sums an iterator of a possible type `T` that can be converted to an integer.
832
833        where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`).
834
835        Example
836        -------
837        ```py
838        numbers: Iterator[str] = Iter(["1", "2", "3"])
839        total = numbers.sum()
840        assert total == 6
841        ```
842        """
843        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:
845    def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None:
846        """Calls `func` on each item in the iterator.
847
848        Example
849        -------
850        ```py
851        iterator = Iter([1, 2, 3])
852        iterator.for_each(lambda item: print(item))
853        # 1
854        # 2
855        # 3
856        ```
857
858        Parameters
859        ----------
860        func: `collections.Callable[[Item], typing.Any]`
861            The function to call on each item in the iterator.
862        """
863        for item in self:
864            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]':
866    async def async_for_each(
867        self,
868        func: collections.Callable[
869            [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem]
870        ],
871    ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]:
872        """Calls the async function on each item in the iterator *concurrently*.
873
874        Concurrently meaning that the next item will not wait for other items
875        to finish to execute, each item gets called in a separate task.
876
877        After all the tasks finish, a `Result[list[T], JoinError]` will be returned,
878        which will need to be handled by the caller.
879
880        Example
881        -------
882        ```py
883        async def create_user(username: str) -> None:
884            await aiohttp.request("POST", f'.../{username}')
885
886        async def main():
887            users = sain.into_iter(["Danny", "Flower"])
888            match await users.async_for_each(lambda username: create_user(username)):
889                case Ok(result):
890                    # all good
891                case Err(why):
892                    print(f"couldn't gather all futures, err={why}")
893        ```
894
895        Parameters
896        ----------
897        func: `collections.Callable[[Item], Coroutine[None, Any, Any]]`
898            The async function to call on each item in the iterator.
899        """
900        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) -> NoReturn:
733@rustc_diagnostic_item("todo")
734def todo(message: LiteralString | None = None) -> typing.NoReturn:
735    """A place holder that indicates unfinished code.
736
737    Example
738    -------
739    ```py
740    from sain import todo
741
742    def from_json(payload: dict[str, int]) -> int:
743        # Calling this function will raise `Error`.
744        todo()
745    ```
746
747    Parameters
748    ----------
749    message : `str | None`
750        Multiple optional arguments to pass if the error was raised.
751    """
752    raise RuntimeWarning(
753        f"not yet implemented: {message}" if message else "not yet implemented"
754    )

A place holder that indicates unfinished code.

Example
from sain import todo

def from_json(payload: dict[str, int]) -> int:
    # Calling this function will raise `Error`.
    todo()
Parameters
  • message (str | None): Multiple optional arguments to pass if the error was raised.
Implementations

This function implements todo in Rust.

@rustc_diagnostic_item('deprecated')
def deprecated( *, obj: 'collections.Callable[P, U] | None' = None, since: "typing.Literal['CURRENT_VERSION'] | LiteralString | None" = None, removed_in: 'LiteralString | None' = None, use_instead: 'LiteralString | None' = None, hint: 'LiteralString | None' = None) -> 'collections.Callable[P, U] | collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
633@rustc_diagnostic_item("deprecated")
634def deprecated(
635    *,
636    obj: collections.Callable[P, U] | None = None,
637    since: typing.Literal["CURRENT_VERSION"] | LiteralString | None = None,
638    removed_in: LiteralString | None = None,
639    use_instead: LiteralString | None = None,
640    hint: LiteralString | None = None,
641) -> (
642    collections.Callable[P, U]
643    | collections.Callable[
644        [collections.Callable[P, U]],
645        collections.Callable[P, U],
646    ]
647):
648    """A decorator that marks a function as deprecated.
649
650    An attempt to call the object that's marked will cause a runtime warn.
651
652    Example
653    -------
654    ```py
655    from sain import deprecated
656
657    @deprecated(
658        since = "1.0.0",
659        removed_in ="3.0.0",
660        use_instead = "UserImpl()",
661        hint = "Hint for ux."
662    )
663    class User:
664        # calling the decorator is not necessary.
665        @deprecated
666        def username(self) -> str:
667            ...
668
669    user = User() # This will cause a warning at runtime.
670
671    ```
672
673    Parameters
674    ----------
675    since : `str`
676        The version that the function was deprecated. the `CURRENT_VERSION` is used internally only.
677    removed_in : `str | None`
678        If provided, It will log when will the object will be removed in.
679    use_instead : `str | None`
680        If provided, This should be the alternative object name that should be used instead.
681    hint: `str`
682        An optional hint for the user.
683    """
684
685    def _create_message(
686        f: typing.Any,
687    ) -> str:
688        msg = f"{_obj_type(f)} `{f.__module__}.{f.__name__}` is deprecated."
689
690        if since is not None:
691            if since == "CURRENT_VERSION":
692                from ._misc import __version__
693
694                msg += " since " + __version__
695            else:
696                msg += " since " + since
697
698        if removed_in:
699            msg += f" Scheduled for removal in `{removed_in}`."
700
701        if use_instead is not None:
702            msg += f" Use `{use_instead}` instead."
703
704        if hint:
705            msg += f" Hint: {hint}"
706        return msg
707
708    def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]:
709        message = _create_message(func)
710
711        @functools.wraps(func)
712        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
713            _warn("\033[93m" + message + "\033[0m", warn_ty=DeprecationWarning)
714            return func(*args, **kwargs)
715
716        # idk why pyright doesn't know the type of wrapper.
717        m = inspect.cleandoc(f"\n# Warning ⚠️\n{message}.")
718        if wrapper.__doc__:
719            # append this message to an existing document.
720            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + m
721        else:
722            wrapper.__doc__ = m
723
724        return wrapper
725
726    # marked only.
727    if obj is not None:
728        return decorator(obj)
729
730    return decorator

A decorator that marks a function as deprecated.

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

Example
from sain import deprecated

@deprecated(
    since = "1.0.0",
    removed_in ="3.0.0",
    use_instead = "UserImpl()",
    hint = "Hint for ux."
)
class User:
    # 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( *, obj: 'collections.Callable[P, U] | None' = None, message: 'LiteralString | None' = None, available_in: 'LiteralString | None' = None) -> 'collections.Callable[P, U] | collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
776@rustc_diagnostic_item("unimplemented")
777def unimplemented(
778    *,
779    obj: collections.Callable[P, U] | None = None,
780    message: LiteralString | None = None,
781    available_in: LiteralString | None = None,
782) -> (
783    collections.Callable[P, U]
784    | collections.Callable[
785        [collections.Callable[P, U]],
786        collections.Callable[P, U],
787    ]
788):
789    """A decorator that marks an object as unimplemented.
790
791    An attempt to call the object that's marked will cause a runtime warn.
792
793    Example
794    -------
795    ```py
796    from sain import unimplemented
797
798    @unimplemented  # Can be used without calling
799    class User:
800        ...
801
802    @unimplemented(message="Not ready", available_in="2.0.0")  # Or with parameters
803    class Config:
804        ...
805    ```
806
807    Parameters
808    ----------
809    message : `str | None`
810        An optional message to be displayed when the function is called. Otherwise default message will be used.
811    available_in : `str | None`
812        If provided, This will be shown as what release this object be implemented.
813    """
814
815    def _create_message(f: typing.Any) -> str:
816        msg = (
817            message
818            or f"{_obj_type(f)} `{f.__module__}.{f.__name__}` is not yet implemented."
819        )
820
821        if available_in:
822            msg += f" Available in `{available_in}`."
823        return msg
824
825    def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]:
826        msg = _create_message(func)
827
828        @functools.wraps(func)
829        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
830            _warn("\033[93m" + msg + "\033[0m", warn_ty=RuntimeWarning)
831            return func(*args, **kwargs)
832
833        m = f"\n# Warning ⚠️\n{msg}."
834        if wrapper.__doc__:
835            # Append the new documentation string to the existing docstring.
836            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + m
837        else:
838            # Assign the new documentation string as the docstring when no existing docstring is present.
839            wrapper.__doc__ = m
840        return wrapper
841
842    if obj is not None:
843        return decorator(obj)
844
845    return decorator

A decorator that marks an object as unimplemented.

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

Example
from sain import unimplemented

@unimplemented  # Can be used without calling
class User:
    ...

@unimplemented(message="Not ready", available_in="2.0.0")  # Or with parameters
class Config:
    ...
Parameters
  • message (str | None): An optional message to be displayed when the function is called. Otherwise default message will be used.
  • available_in (str | None): If provided, This will be shown as what release this object be implemented.
Implementations

This function implements unimplemented in Rust.

@rustc_diagnostic_item('doc')
def doc( path: 'Read') -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
848@rustc_diagnostic_item("doc")
849def doc(
850    path: Read,
851) -> collections.Callable[
852    [collections.Callable[P, U]],
853    collections.Callable[P, U],
854]:
855    """Set `path` to be the object's documentation.
856
857    Example
858    -------
859    ```py
860    from sain import doc
861    from pathlib import Path
862
863    @doc(Path("../README.md"))
864    class builtins:
865        @doc("bool.html")
866        def bool_docs() -> None:
867            ...
868    ```
869
870    Parameters
871    ----------
872    path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]`
873        The path to read the content from.
874    """
875
876    def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]:
877        with open(path, "r") as file:
878            f.__doc__ = file.read()
879
880        return lambda *args, **kwargs: f(*args, **kwargs)
881
882    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':
503@rustc_diagnostic_item("include_str")
504def include_str(file: LiteralString) -> LiteralString:
505    """Includes a file as literal `str`.
506
507    This function is not magic, It is literally defined as
508
509    ```py
510    with open(file, "r") as f:
511        return f.read()
512    ```
513
514    The file name can may be either a relative to the current file or a complete path.
515
516    Example
517    -------
518    ```py
519    from sain.macros import include_str
520
521    def entry() -> None:
522        ...
523
524    entry.__doc__ = include_str("README.md")
525
526    ```
527    """
528    with open(file, "r") as buf:
529        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:
473@rustc_diagnostic_item("include_bytes")
474def include_bytes(file: LiteralString) -> bytes:
475    """Includes a file as `bytes`.
476
477    This function is not magic, It is literally defined as
478
479    ```py
480    with open(file, "rb") as f:
481        return f.read()
482    ```
483
484    The file name can may be either a relative to the current file or a complete path.
485
486    Example
487    -------
488    File "spanish.in":
489    ```text
490    adiós
491    ```
492    File "main.py":
493    ```py
494    from sain.macros import include_bytes
495    buffer = include_bytes("spanish.in")
496    assert buffer.decode() == "adiós"
497    ```
498    """
499    with open(file, "rb") as buf:
500        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:
433@rustc_diagnostic_item("assert_eq")
434def assert_eq(left: T, right: T) -> None:
435    """Asserts that two expressions are equal to each other.
436
437    This exactly as `assert left == right`, but includes a useful message in case of failure.
438
439    Example
440    -------
441    ```py
442    from sain.macros import assert_eq
443    a = 3
444    b = 1 + 2
445    assert_eq(a, b)
446    ```
447    """
448    assert left == right, (
449        f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"'
450    )

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:
453@rustc_diagnostic_item("assert_ne")
454def assert_ne(left: T, right: T) -> None:
455    """Asserts that two expressions are not equal to each other.
456
457    This exactly as `assert left == right`, but includes a useful message in case of failure.
458
459    Example
460    -------
461    ```py
462    from sain.macros import assert_ne
463    a = 3
464    b = 2 + 2
465    assert_ne(a, b)
466    ```
467    """
468    assert left != right, (
469        f'assertion `left != right` failed\nleft: "{left!r}"\nright: "{right!r}"'
470    )

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

A contiguous growable alternative to the builtin list with extra functionalities.

The layout of Vec<T> is exactly the same as list<T>. Which means list<T>'s methods are inherited into Vec<T>.

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

print(names) # ['foo', 'bar']
assert names.len() == 2
Constructing
  • Vec(): Create an empty vec, this will not allocate the initial buffer.
  • Vec(other_list): Create a vec which points to other_list
  • Vec([1, 2, 3]): Create a vec with [1, 2, 3] pre-allocated.
  • Vec(iterable): Create a vec from an iterable, This is O(n) where n is the number of elements, since it copies iterable's elements into a new list.
  • Vec.with_capacity(5): Create a vec that can hold up to 5 elements

Iterating over Vec

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

for i in names:
    print(i)

# foo
# bar

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

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

# FOO
# BAR

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

Comparison operators

A Vec may be compared with another Vec or a list, any other type will return False

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

Zero-Copy

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

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

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

If you want an owned Vec that doesn't point to the original list, copy the list into a new Vec.

from sain import Vec

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

vec[1] # IndexError: "bar" doesn't exist in vec.
Implementations

This class implements Vec in Rust.

Vec(iterable: Iterable[~T] | None = None)
169    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
170        """Create an empty `Vec<T>`.
171
172        The vector will not allocate until elements are pushed onto it.
173
174        Example
175        ------
176        ```py
177        vec: Vec[float] = Vec()
178        ```
179        """
180        # We won't allocate to build the list here.
181        # Instead, On first push.
182        if isinstance(iterable, list):
183            # Calling `list()` on another list will copy it, So instead we just point to it.
184            self._ptr = iterable
185        elif isinstance(iterable, Vec):
186            self._ptr = iterable._ptr
187        # any other iterable that ain't a list needs to get copied into a new list.
188        else:
189            self._ptr: list[T] | None = list(iterable) if iterable else None
190
191        self._capacity: int | None = None

Create an empty Vec<T>.

The vector will not allocate until elements are pushed onto it.

Example
vec: Vec[float] = Vec()
@classmethod
def with_capacity(cls, capacity: int) -> Vec[~T]:
193    @classmethod
194    def with_capacity(cls, capacity: int) -> Vec[T]:
195        """Create a new `Vec` with at least the specified capacity.
196        This vec will be able to hold `capacity` elements without pushing further.
197
198        The capacity may dynamically grow if `Vec.reserve` is called at runtime.
199
200        Check out `Vec.push_within_capacity` as well.
201
202        Example
203        -------
204        ```py
205        vec = Vec.with_capacity(3)
206        assert vec.len() == 0 and vec.capacity() >= 3
207
208        vec.push(1)
209        vec.push(2)
210        vec.push(3)
211        print(vec.len()) # 3
212
213        # This won't push.
214        vec.push(4)
215        ```
216        """
217        v = cls()
218        v._capacity = capacity
219        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_ref(self) -> sain.collections.slice.Slice[~T]:
221    def as_ref(self) -> Slice[T]:
222        """Return an immutable view over this vector elements.
223
224        No copying occurs.
225
226        Example
227        -------
228        ```py
229        def send_bytes(v: Slice[int]) -> None:
230            # access `vec` in a read-only mode.
231            socket.write_all(v)
232
233        buf = Vec([1, 2, 3])
234        send_bytes(buf.as_ref())
235        ```
236        """
237        return Slice(self)

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_ref())
def as_mut(self) -> sain.collections.slice.SliceMut[~T]:
239    def as_mut(self) -> SliceMut[T]:
240        """Return a mutable view over this vector elements.
241
242        No copying occurs.
243
244        Example
245        -------
246        ```py
247        def read_bytes(buf: SliceMut[int]) -> None:
248            # similar to how `Write::write_to_end` requires a `&mut [u8]`
249            socket.read_to_end(buf)
250
251        buf: Vec[int] = Vec()
252        read_bytes(buf.as_mut()) # or just `buf`
253        assert vec.len() >= 1
254        ```
255        """
256        return SliceMut(self)

Return a mutable view over this vector elements.

No copying occurs.

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()) # or just `buf`
assert vec.len() >= 1
def len(self) -> int:
258    def len(self) -> int:
259        """Return the number of elements in this vector.
260
261        Example
262        -------
263        ```py
264        vec = Vec((1,2,3))
265
266        assert vec.len() == 3
267        ```
268        """
269        return self.__len__()

Return the number of elements in this vector.

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

assert vec.len() == 3
def capacity(self) -> int:
271    def capacity(self) -> int:
272        """Return the capacity of this vector if set, 0 if not .
273
274        The number `0` here has two different Meanings:
275        - The first means the `Vec` doesn't have a specific capacity set.
276        - The second means the `Vec` literally initialized with `0` capacity.
277
278        Example
279        -------
280        ```py
281        vec_with_cap = Vec.with_capacity(3)
282        assert vec_with_cap.capacity().unwrap() == 3
283
284        vec = Vec([1, 2, 3])
285        assert vec.capacity() == 0
286        ```
287        """
288        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 iter(self) -> sain.iter.TrustedIter[~T]:
290    def iter(self) -> _iter.TrustedIter[T]:
291        """Returns an iterator over this vector elements.
292
293        Example
294        -------
295        ```py
296        vec = Vec([1 ,2, 3])
297        for element in vec.iter():
298            print(element)
299        ```
300        """
301        return _iter.TrustedIter(self._ptr or ())

Returns an iterator over this vector elements.

Example
vec = Vec([1 ,2, 3])
for element in vec.iter():
    print(element)
def is_empty(self) -> bool:
303    def is_empty(self) -> bool:
304        """Returns true if the vector contains no elements.
305
306        Inlined into:
307        ```py
308        not self.vec
309        ```
310        """
311        return not self._ptr

Returns true if the vector contains no elements.

Inlined into:

not self.vec
def leak(self) -> list[~T]:
313    def leak(self) -> list[T]:
314        """Consumes and leaks the Vec, returning a mutable reference to the contents.
315
316        After calling this, this vec will no longer reference the underlying buffer,
317        therefore, it becomes unusable.
318
319        if `self` is uninitialized, an empty list is returned.
320
321        This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without
322        any copies.
323
324        Example
325        -------
326        ```py
327        x = Vec([1, 2, 3])
328        owned = x.leak()
329        # x is now unusable.
330        owned[0] += 1
331        assert owned == [2, 2, 3]
332        ```
333        """
334        if self._ptr is not None:
335            # don't point to `_ptr` anymore.
336            tmp = self._ptr
337            del self._ptr
338            return tmp
339
340        return []

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]:
342    def split_off(self, at: int) -> Vec[T]:
343        """Split the vector off at the specified position, returning a new
344        vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`.
345
346        if this vec is empty, `self` is returned unchanged.
347
348        Example
349        -------
350        ```py
351        origin = Vec((1, 2, 3, 4))
352        split = vec.split_off(2)
353
354        print(origin, split)  # [1, 2], [3, 4]
355        ```
356
357        Raises
358        ------
359        `IndexError`
360            This method will raise if `at` > `len(self)`
361        """
362        len_ = self.len()
363        if at > len_:
364            raise IndexError(
365                f"Index `at` ({at}) should be <= than len of vector ({len_}) "
366            ) from None
367
368        # Either the list is empty or uninit.
369        if not self._ptr:
370            return self
371
372        split = self[at:len_]  # split the items into a new vec.
373        del self._ptr[at:len_]  # remove the items from the original list.
374        return split

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

if this vec is empty, self is returned unchanged.

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

print(origin, split)  # [1, 2], [3, 4]
Raises
  • IndexError: This method will raise if at > len(self)
def split_first(self) -> '_option.Option[tuple[T, collections.Sequence[T]]]':
376    def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
377        """Split the first and rest elements of the vector, If empty, `None` is returned.
378
379        Example
380        -------
381        ```py
382        vec = Vec([1, 2, 3])
383        split = vec.split_first()
384        assert split == Some((1, [2, 3]))
385
386        vec: Vec[int] = Vec()
387        split = vec.split_first()
388        assert split.is_none()
389        ```
390        """
391        if not self._ptr:
392            return _option.NOTHING
393
394        # optimized to only one element in the vector.
395        if self.len() == 1:
396            return _option.Some((self[0], ()))
397
398        first, *rest = self._ptr
399        return _option.Some((first, rest))

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

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

vec: Vec[int] = Vec()
split = vec.split_first()
assert split.is_none()
def split_last(self) -> '_option.Option[tuple[T, collections.Sequence[T]]]':
401    def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
402        """Split the last and rest elements of the vector, If empty, `None` is returned.
403
404        Example
405        -------
406        ```py
407        vec = Vec([1, 2, 3])
408        last, rest = vec.split_last().unwrap()
409        assert (last, rest) == [3, [1, 2]]
410        ```
411        """
412        if not self._ptr:
413            return _option.NOTHING
414
415        # optimized to only one element in the vector.
416        if self.len() == 1:
417            return _option.Some((self[0], ()))
418
419        last, *rest = self._ptr[-1], *self._ptr[:-1]
420        return _option.Some((last, rest))

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

Example
vec = Vec([1, 2, 3])
last, rest = vec.split_last().unwrap()
assert (last, rest) == [3, [1, 2]]
def split_at( self, mid: int) -> tuple[Vec[~T], Vec[~T]]:
422    def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]:
423        """Divide `self` into two at an index.
424
425        The first will contain all elements from `[0:mid]` excluding `mid` it self.
426        and the second will contain the remaining elements.
427
428        if `mid` > `self.len()`, Then all elements will be moved to the left,
429        returning an empty vec in right.
430
431        Example
432        -------
433        ```py
434        buffer = Vec((1, 2, 3, 4))
435        left, right = buffer.split_at(0)
436        assert left == [] and right == [1, 2, 3, 4]
437
438        left, right = buffer.split_at(2)
439        assert left == [1, 2] and right == [2, 3]
440        ```
441
442        The is roughly the implementation
443        ```py
444        vec[0:mid], vec[mid:]
445        ```
446        """
447        return self[0:mid], self[mid:]

Divide self into two at an index.

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

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

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

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

The is roughly the implementation

vec[0:mid], vec[mid:]
def swap(self, a: int, b: int):
449    def swap(self, a: int, b: int):
450        """Swap two elements in the vec.
451
452        if `a` equals to `b` then it's guaranteed that elements won't change value.
453
454        Example
455        -------
456        ```py
457        buf = Vec([1, 2, 3, 4])
458        buf.swap(0, 3)
459        assert buf == [4, 2, 3, 1]
460        ```
461
462        Raises
463        ------
464        IndexError
465            If the positions of `a` or `b` are out of index.
466        """
467        if self[a] == self[b]:
468            return
469
470        self[a], self[b] = self[b], self[a]

Swap two elements in the vec.

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

Example
buf = Vec([1, 2, 3, 4])
buf.swap(0, 3)
assert buf == [4, 2, 3, 1]
Raises
  • IndexError: If the positions of a or b are out of index.
def swap_unchecked(self, a: int, b: int):
472    def swap_unchecked(self, a: int, b: int):
473        """Swap two elements in the vec. without checking if `a` == `b`.
474
475        If you care about `a` and `b` equality, see `Vec.swap`.
476
477        Example
478        -------
479        ```py
480        buf = Vec([1, 2, 3, 1])
481        buf.swap_unchecked(0, 3)
482        assert buf == [1, 2, 3, 1]
483        ```
484
485        Raises
486        ------
487        IndexError
488            If the positions of `a` or `b` are out of index.
489        """
490        self[a], self[b] = self[b], self[a]

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

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

Example
buf = Vec([1, 2, 3, 1])
buf.swap_unchecked(0, 3)
assert buf == [1, 2, 3, 1]
Raises
  • IndexError: If the positions of a or b are out of index.
def first(self) -> '_option.Option[T]':
492    def first(self) -> _option.Option[T]:
493        """Get the first element in this vec, returning `None` if there's none.
494
495        Example
496        -------
497        ```py
498        vec = Vec((1,2,3))
499        first = vec.first()
500        assert ~first == 1
501        ```
502        """
503        return self.get(0)

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

Example
vec = Vec((1,2,3))
first = vec.first()
assert ~first == 1
def last(self) -> '_option.Option[T]':
505    def last(self) -> _option.Option[T]:
506        """Get the last element in this vec, returning `None` if there's none.
507
508        Example
509        -------
510        ```py
511        vec = Vec([1, 2, 3, 4])
512        last = vec.last()
513        assert ~last == 4
514        ```
515        """
516        return self.get(-1)

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

Example
vec = Vec([1, 2, 3, 4])
last = vec.last()
assert ~last == 4
def truncate(self, size: int) -> None:
518    def truncate(self, size: int) -> None:
519        """Shortens the vec, keeping the first `size` elements and dropping the rest.
520
521        Example
522        -------
523        ```py
524        vec = Vec([1,2,3])
525        vec.truncate(1)
526        assert vec == [1]
527        ```
528        """
529        if not self._ptr:
530            return
531
532        del self._ptr[size:]

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

Example
vec = Vec([1,2,3])
vec.truncate(1)
assert vec == [1]
def retain(self, f: Callable[[~T], bool]) -> None:
534    def retain(self, f: collections.Callable[[T], bool]) -> None:
535        """Retains only the elements specified by the predicate.
536
537        This operation occurs in-place without copying the original list.
538
539        Example
540        -------
541        ```py
542        vec = Vec([1, 2, 3])
543        vec.retain(lambda elem: elem > 1)
544
545        assert vec == [2, 3]
546        ```
547
548        The impl for this method is very simple
549        ```py
550        for idx, e in enumerate(vec):
551            if not f(e):
552                del vec[idx]
553        ```
554        """
555        if not self._ptr:
556            return
557
558        idx = 0
559        while idx < len(self._ptr):
560            if not f(self._ptr[idx]):
561                del self._ptr[idx]
562            else:
563                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:
565    def swap_remove(self, item: T) -> T:
566        """Remove the first appearance of `item` from this vector and return it.
567
568        Raises
569        ------
570        * `ValueError`: if `item` is not in this vector.
571        * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it.
572
573        Example
574        -------
575        ```py
576        vec = Vec(('a', 'b', 'c'))
577        element = vec.swap_remove('a')
578        assert vec == ['b', 'c'] and element == 'a'
579        ```
580        """
581        if self._ptr is None:
582            raise MemoryError("Vec is unallocated.") from None
583
584        return self._ptr.pop(self.index(item))

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

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

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

Example
vec = Vec(('a', 'b', 'c'))
element = vec.swap_remove('a')
assert vec == ['b', 'c'] and element == 'a'
def fill(self, value: ~T) -> None:
586    def fill(self, value: T) -> None:
587        """Fill `self` with the given `value`.
588
589        Nothing happens if the vec is empty or unallocated.
590
591        Example
592        -------
593        ```py
594        a = Vec([0, 1, 2, 3])
595        a.fill(0)
596        assert a == [0, 0, 0, 0]
597        ```
598        """
599        ptr = self._ptr
600        if not ptr:
601            return
602
603        ptr[:] = [value] * len(ptr)

Fill self with the given value.

Nothing happens if the vec is empty or unallocated.

Example
a = Vec([0, 1, 2, 3])
a.fill(0)
assert a == [0, 0, 0, 0]
def push(self, item: ~T) -> None:
605    def push(self, item: T) -> None:
606        """Push an element at the end of the vector.
607
608        Example
609        -------
610        ```py
611        vec = Vec()
612        vec.push(1)
613
614        assert vec == [1]
615        ```
616        """
617        if self._capacity is not None:
618            self.push_within_capacity(item)
619            return
620
621        if self._ptr is None:
622            self._ptr = []
623
624        self._ptr.append(item)

Push an element at the end of the vector.

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

assert vec == [1]
def push_within_capacity(self, x: ~T) -> 'Result[None, T]':
626    def push_within_capacity(self, x: T) -> Result[None, T]:
627        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
628
629        Example
630        -------
631        ```py
632        vec: Vec[int] = Vec.with_capacity(3)
633        for i in range(3):
634            match vec.push_within_capacity(i):
635                case Ok(_):
636                    print("All good.")
637                case Err(elem):
638                    print("Reached max cap :< can't push", elem)
639        ```
640
641        Or you can also just call `Vec.push` and it will push if there's is sufficient capacity.
642        ```py
643        vec: Vec[int] = Vec.with_capacity(3)
644
645        vec.extend((1, 2, 3))
646        vec.push(4)
647
648        assert vec.len() == 3
649        ```
650        """
651        if self._ptr is None:
652            self._ptr = []
653
654        if self.len() == self._capacity:
655            return _result.Err(x)
656
657        self._ptr.append(x)
658        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:
660    def reserve(self, additional: int) -> None:
661        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
662
663        Example
664        -------
665        ```py
666        vec = Vec.with_capacity(3)
667
668        for i in range(3):
669            vec.push(i)
670
671        match vec.push_within_capacity(4):
672            case Ok(_):
673                print("Pushed 4 successfully.")
674            case Err(elem):
675                vec.reserve(1)  # Reserve capacity for 1 more element.
676                vec.push(4)  # Now we can push 4.
677        ```
678        """
679        if self._capacity is not None:
680            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:
682    def shrink_to_fit(self) -> None:
683        """Shrink the capacity of this `Vec` to match its length.
684
685        If `self` is initialized with no capacity, This is a `NOP`.
686
687        Example
688        -------
689        ```py
690        s = Vec([1, 2, 3, 4])
691
692        s.reserve(100)
693        s.shrink_to_fit()
694        assert s.capacity() == 4
695        ```
696        """
697        if self._capacity is None:
698            return
699
700        # The capacity is never less than the length.
701        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:
703    def shrink_to(self, min_capacity: int) -> None:
704        """Shrink the capacity of this `Vec` to a minimum specified capacity.
705
706        If `self` is initialized with no capacity or the current capacity is less than the lower limit,
707        This is a `NOP`.
708
709        Example
710        -------
711        ```py
712        vec = Vec.with_capacity(10)
713        vec.extend([1, 2, 3])
714        assert vec.capacity() >= 10
715        vec.shrink_to(4)
716        assert vec.capacity() >= 4
717        vec.shrink_to(0)
718        ```
719        """
720        if self._capacity is None or self._capacity <= min_capacity:
721            return
722
723        # Ensure the capacity is not reduced below the current length of the vector.
724        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:
730    def append(self, value: T) -> None:
731        """An alias to `Vec.push`."""
732        self.push(value)

An alias to Vec.push.

def get(self, index: int) -> '_option.Option[T]':
734    def get(self, index: int) -> _option.Option[T]:
735        """Get the item at the given index, or `Some[None]` if its out of bounds.
736
737        Example
738        -------
739        ```py
740        vec = Vec((1, 2, 3))
741        vec.get(0) == Some(1)
742        vec.get(3) == Some(None)
743        ```
744        """
745        try:
746            return _option.Some(self[index])
747        except IndexError:
748            return _option.NOTHING

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

Example
vec = Vec((1, 2, 3))
vec.get(0) == Some(1)
vec.get(3) == Some(None)
def insert(self, index: int, value: ~T) -> None:
750    def insert(self, index: int, value: T) -> None:
751        """Insert an element at the position `index`.
752
753        Example
754        --------
755        ```py
756        vec = Vec((2, 3))
757        vec.insert(0, 1)
758        assert vec == [1, 2, 3]
759        ```
760        """
761        self.__setitem__(index, value)

Insert an element at the position index.

Example
vec = Vec((2, 3))
vec.insert(0, 1)
assert vec == [1, 2, 3]
def pop(self, index: int = -1) -> '_option.Option[T]':
763    def pop(self, index: int = -1) -> _option.Option[T]:
764        """Removes the last element from a vector and returns it, or `None` if it is empty.
765
766        Example
767        -------
768        ```py
769        vec = Vec((1, 2, 3))
770        assert vec.pop() == Some(3)
771        assert vec == [1, 2]
772        ```
773        """
774        if not self._ptr:
775            return _option.NOTHING
776
777        return _option.Some(self._ptr.pop(index))

Removes the last element from a 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]':
779    def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]:
780        """Removes the last element from a vector and returns it if `f` returns `True`,
781        or `None` if it is empty.
782
783        Example
784        -------
785        ```py
786        vec = Vec((1, 2, 3))
787        assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
788        assert vec == [1, 2]
789        ```
790        """
791        if not self._ptr:
792            return _option.NOTHING
793
794        if pred(self[-1]):
795            return self.pop()
796
797        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:
799    def dedup(self) -> None:
800        """Removes consecutive repeated elements in the vector according to their `__eq__`
801        implementation.
802
803        Example
804        -------
805        ```py
806        vec = Vec([1, 2, 2, 3, 2])
807        vec.dedup()
808        assert vec == [1, 2, 3, 2]
809        """
810        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:
812    def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None:
813        """Removes all but the first of consecutive elements in the vector satisfying a given equality.
814
815        Example
816        -------
817        ```py
818        vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
819        vec.dedup_by(lambda a, b: a.lower() == b.lower())
820        assert vec == ["foo", "bar", "baz", "bar"]
821        ```
822
823        Only remove consecutive duplicates.
824        ```py
825        vec = Vec([1, 2, 2, 3, 4, 1])
826        vec.dedup_by(lambda a, b: a == b)
827        # The final 1 is not adjacent to the first 1, so it is not removed.
828        assert vec == [1, 2, 3, 4, 1]
829        ```
830        """
831
832        if not self._ptr or (len_ := len(self._ptr)) <= 1:
833            return
834
835        idx = 1
836        while idx < len_:
837            if same_bucket(self._ptr[idx], self._ptr[idx - 1]):
838                del self._ptr[idx]
839                len_ -= 1
840            else:
841                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:
843    def remove(self, item: T) -> None:
844        """Remove the first appearance of `item` from this vector.
845
846        Example
847        -------
848        ```py
849        vec = Vec(('a', 'b', 'c'))
850        vec.remove('a')
851        assert vec == ['b', 'c']
852        ```
853        """
854        if not self._ptr:
855            return
856
857        self._ptr.remove(item)

Remove the first appearance of item from this vector.

Example
vec = Vec(('a', 'b', 'c'))
vec.remove('a')
assert vec == ['b', 'c']
def extend(self, iterable: Iterable[~T]) -> None:
859    def extend(self, iterable: collections.Iterable[T]) -> None:
860        """Extend this vector from another iterable.
861
862        Example
863        -------
864        ```py
865        vec = Vec((1, 2, 3))
866        vec.extend((4, 5, 6))
867
868        assert vec == [1, 2, 3, 4, 5, 6]
869        ```
870        """
871        if self._ptr is None:
872            self._ptr = []
873
874        self._ptr.extend(iterable)

Extend this vector from another iterable.

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

assert vec == [1, 2, 3, 4, 5, 6]
def copy(self) -> Vec[~T]:
876    def copy(self) -> Vec[T]:
877        """Copy all elements of `self` into a new vector.
878
879        Example
880        -------
881        ```py
882        original = Vec((1,2,3))
883        copy = original.copy()
884        copy.push(4)
885
886        print(original) # [1, 2, 3]
887        ```
888        """
889        return Vec(self._ptr[:]) if self._ptr else Vec()

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:
891    def clear(self) -> None:
892        """Clear all elements of this vector.
893
894        Example
895        -------
896        ```py
897        vec = Vec((1,2,3))
898        vec.clear()
899        assert vec.len() == 0
900        ```
901        """
902        if not self._ptr:
903            return
904
905        self._ptr.clear()

Clear all elements of this vector.

Example
vec = Vec((1,2,3))
vec.clear()
assert vec.len() == 0
def sort( self, *, key: 'collections.Callable[[T], SupportsRichComparison] | None' = None, reverse: bool = False) -> None:
907    def sort(
908        self,
909        *,
910        key: collections.Callable[[T], SupportsRichComparison] | None = None,
911        reverse: bool = False,
912    ) -> None:
913        """This method sorts the list in place, using only < comparisons between items.
914
915        Example
916        -------
917        ```py
918        vec = Vec((2, 1, 3))
919        vec.sort()
920        assert vec == [1, 2, 3]
921        ```
922        """
923        if not self._ptr:
924            return
925
926        # key can be `None` here just fine, idk why pyright is complaining.
927        self._ptr.sort(key=key, reverse=reverse)  # pyright: ignore

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

Example
vec = Vec((2, 1, 3))
vec.sort()
assert vec == [1, 2, 3]
def index( self, item: ~T, start: <class 'SupportsIndex'> = 0, end: int = 9223372036854775807) -> int:
929    def index(
930        self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize
931    ) -> int:
932        # << Official documentation >>
933        """Return zero-based index in the vec of the first item whose value is equal to `item`.
934        Raises a ValueError if there is no such item.
935
936        Example
937        -------
938        ```py
939        vec = Vec((1, 2, 3))
940        assert vec.index(2) == 1
941        ```
942        """
943        if self._ptr is None:
944            raise ValueError from None
945
946        return self._ptr.index(item, start, end)

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

Example
vec = Vec((1, 2, 3))
assert vec.index(2) == 1
def count(self, item: ~T) -> int:
948    def count(self, item: T) -> int:
949        """Return the number of occurrences of `item` in the vec.
950
951        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
952
953        Example
954        --------
955        ```py
956        vec = Vec((1, 2, 3, 3))
957        assert vec.count(3) == 2
958        ```
959        """
960        if self._ptr is None:
961            return 0
962
963        return self._ptr.count(item)

Return the number of occurrences of item in the vec.

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

Example
vec = Vec((1, 2, 3, 3))
assert vec.count(3) == 2
@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'