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    # cfg.py
 39    "cfg",
 40    "cfg_attr",
 41    # default.py
 42    "default",
 43    "Default",
 44    # option.py
 45    "option",
 46    "Some",
 47    "Option",
 48    "NOTHING",
 49    # iter.py
 50    "Iter",
 51    "Iterator",
 52    "iter",
 53    # macros.py
 54    "macros",
 55    "todo",
 56    "deprecated",
 57    "unimplemented",
 58    "doc",
 59    "include_str",
 60    "include_bytes",
 61    "assert_eq",
 62    "assert_ne",
 63    # futures.py
 64    "futures",
 65    # result.py
 66    "result",
 67    "Ok",
 68    "Err",
 69    "Result",
 70    # collections
 71    "collections",
 72    "Vec",
 73    # error.py
 74    "error",
 75    "Error",
 76    # boxed.py
 77    "boxed",
 78    "Box",
 79    # sync
 80    "sync",
 81    # maybe_uninit.py
 82    "maybe_uninit",
 83    # convert
 84    "convert",
 85    "From",
 86    "TryFrom",
 87    "Into",
 88    "TryInto",
 89    "ToString",
 90    # time
 91    "time",
 92    # misc
 93    "__version__",
 94    "__url__",
 95    "__author__",
 96    "__about__",
 97    "__license__",
 98)
 99
100from . import boxed
101from . import collections
102from . import convert
103from . import default
104from . import error
105from . import futures
106from . import iter
107from . import macros
108from . import maybe_uninit
109from . import option
110from . import result
111from . import sync
112from . import time
113from ._misc import __about__
114from ._misc import __author__
115from ._misc import __license__
116from ._misc import __url__
117from ._misc import __version__
118from .boxed import Box
119from .cfg import cfg
120from .cfg import cfg_attr
121from .collections import Vec
122from .convert import From
123from .convert import Into
124from .convert import ToString
125from .convert import TryFrom
126from .convert import TryInto
127from .default import Default
128from .error import Error
129from .iter import Iter
130from .iter import Iterator
131from .macros import assert_eq
132from .macros import assert_ne
133from .macros import deprecated
134from .macros import doc
135from .macros import include_bytes
136from .macros import include_str
137from .macros import todo
138from .macros import unimplemented
139from .option import NOTHING
140from .option import Option
141from .option import Some
142from .result import Err
143from .result import Ok
144from .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:
@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:
@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    @rustc_diagnostic_item("default_fn")
88    @staticmethod
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)
@rustc_diagnostic_item('default_fn')
@staticmethod
def default() -> +_T_co:
87    @rustc_diagnostic_item("default_fn")
88    @staticmethod
89    def default() -> _T_co:
90        """Return the default value of the object."""
91        raise NotImplementedError

Return the default value of the object.

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

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

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

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

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

assert it.count() == 0
assert sort == ["a", "b", "c"]
def next(self) -> 'Option[Item]':
303    def next(self) -> Option[Item]:
304        """Advance the iterator, Returning the next item, `Some(None)` if all items yielded.
305
306        Example
307        -------
308        ```py
309        iterator = Iter(["1", "2"])
310        assert iterator.next() == Some("1")
311        assert iterator.next() == Some("2")
312        assert iterator.next().is_none()
313        ```
314        """
315        try:
316            return _option.Some(self.__next__())
317        except StopIteration:
318            # ! SAFETY: No more items in the iterator.
319            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]:
321    def cloned(self) -> Cloned[Item]:
322        """Creates an iterator which shallow copies its elements by reference.
323
324        If you need a copy of the actual iterator and not the elements.
325        use `Iter.clone()`
326
327        .. note::
328            This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html)
329            on each item that is being yielded.
330
331        Example
332        -------
333        ```py
334        @dataclass
335        class User:
336            users_ids: list[int] = []
337
338        # An iterator which elements points to the same user.
339        user = User()
340        it = Iter((user, user))
341
342        for u in it.cloned():
343            u.user_ids.append(1)
344
345        # We iterated over the same user pointer twice and appended "1"
346        # since `copy` returns a shallow copy of nested structures.
347        assert len(user.user_ids) == 2
348        ```
349        """
350        return Cloned(self)

Creates an iterator which shallow copies its elements by reference.

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

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

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

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

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

# We iterated over the same user pointer twice and appended "1"
# since `copy` returns a shallow copy of nested structures.
assert len(user.user_ids) == 2
def copied(self) -> sain.iter.Copied[~Item]:
352    def copied(self) -> Copied[Item]:
353        """Creates an iterator which copies all of its elements by value.
354
355        If you only need a copy of the item reference, Use `.cloned()` instead.
356
357        .. note::
358            This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html)
359            on each item that is being yielded.
360
361        Example
362        -------
363        ```py
364        @dataclass
365        class User:
366            users_ids: list[int] = []
367
368        # An iterator which elements points to the same user.
369        user = User()
370        it = Iter((user, user))
371
372        for u in it.copied():
373            # A new list is created for each item.
374            u.user_ids.append(1)
375
376        # The actual list is untouched since we consumed a deep copy of it.
377        assert len(user.user_ids) == 0
378        ```
379        """
380        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]:
382    def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]:
383        """Maps each item in the iterator to another type.
384
385        Example
386        -------
387        ```py
388        iterator = Iter(["1", "2", "3"]).map(int)
389
390        for item in iterator:
391            assert isinstance(item, int)
392        ```
393
394        Parameters
395        ----------
396        predicate: `Callable[[Item], OtherItem]`
397            The function to map each item in the iterator to the other type.
398        """
399        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]:
401    def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]:
402        """Filters the iterator to only yield items that match the predicate.
403
404        Example
405        -------
406        ```py
407        places = Iter(['London', 'Paris', 'Los Angeles'])
408        for place in places.filter(lambda place: place.startswith('L')):
409            print(place)
410
411        # London
412        # Los Angeles
413        ```
414        """
415        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]:
417    def take(self, count: int) -> Take[Item]:
418        """Take the first number of items until the number of items
419        are yielded or the end of the iterator is exhausted.
420
421        Example
422        -------
423        ```py
424        iterator = Iter(['c', 'x', 'y'])
425
426        for x in iterator.take(2):
427            assert x in ('c', 'x')
428
429        # <Iter(['c', 'x'])>
430        ```
431        """
432        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]:
434    def skip(self, count: int) -> Skip[Item]:
435        """Skips the first number of items in the iterator.
436
437        Example
438        -------
439        ```py
440        iterator = Iter((1, 2, 3, 4))
441        for i in iterator.skip(2):
442            print(i)
443
444        # 3
445        # 4
446        ```
447        """
448        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]:
450    def enumerate(self, *, start: int = 0) -> Enumerate[Item]:
451        """Create a new iterator that yields a tuple of the index and item.
452
453        Example
454        -------
455        ```py
456        iterator = Iter([1, 2, 3])
457        for index, item in iterator.enumerate():
458            print(index, item)
459
460        # 0 1
461        # 1 2
462        # 2 3
463        ```
464        """
465        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]:
467    def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]:
468        """yields items from the iterator while predicate returns `True`.
469
470        The rest of the items are discarded as soon as the predicate returns `False`
471
472        Example
473        -------
474        ```py
475        iterator = Iter(['a', 'ab', 'xd', 'ba'])
476        for x in iterator.take_while(lambda x: 'a' in x):
477            print(x)
478
479        # a
480        # ab
481        ```
482
483        Parameters
484        ----------
485        predicate: `collections.Callable[[Item], bool]`
486            The function to predicate each item in the iterator.
487        """
488        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]:
490    def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]:
491        """Yields items from the iterator while predicate returns `False`.
492
493        Example
494        -------
495        ```py
496        iterator = Iter(['a', 'ab', 'xd', 'ba'])
497        for x in iterator.drop_while(lambda x: 'a' in x):
498            print(x)
499
500        # xd
501        # ba
502        ```
503
504        Parameters
505        ----------
506        predicate: `collections.Callable[[Item], bool]`
507            The function to predicate each item in the iterator.
508        """
509        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]:
511    def chunks(self, chunk_size: int, /) -> Chunks[Item]:
512        """Returns an iterator over `chunk_size` elements of the iterator at a time,
513        starting at the beginning of the iterator.
514
515        Example
516        -------
517        ```py
518        iter = Iter(['a', 'b', 'c', 'd', 'e'])
519        chunks = iter.chunks()
520        assert chunks.next().unwrap() == ['a', 'b']
521        assert chunks.next().unwrap() == ['c', 'd']
522        assert chunks.next().unwrap() == ['e']
523        assert chunks.next().is_none()
524        ```
525        """
526        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:
528    def all(self, predicate: collections.Callable[[Item], bool]) -> bool:
529        """Return `True` if all items in the iterator match the predicate.
530
531        Example
532        -------
533        ```py
534        iterator = Iter([1, 2, 3])
535        while iterator.all(lambda item: isinstance(item, int)):
536            print("Still all integers")
537            continue
538            # Still all integers
539        ```
540
541        Parameters
542        ----------
543        predicate: `collections.Callable[[Item], bool]`
544            The function to test each item in the iterator.
545        """
546        return all(predicate(item) for item in self)

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

Example
iterator = Iter([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
    print("Still all integers")
    continue
    # Still all integers
Parameters
  • predicate (collections.Callable[[Item], bool]): The function to test each item in the iterator.
def any(self, predicate: Callable[[~Item], bool]) -> bool:
548    def any(self, predicate: collections.Callable[[Item], bool]) -> bool:
549        """`True` if any items in the iterator match the predicate.
550
551        Example
552        -------
553        ```py
554        iterator = Iter([1, 2, 3])
555        if iterator.any(lambda item: isinstance(item, int)):
556            print("At least one item is an int.")
557        # At least one item is an int.
558        ```
559
560        Parameters
561        ----------
562        predicate: `collections.Callable[[Item], bool]`
563            The function to test each item in the iterator.
564        """
565        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]]:
567    def zip(
568        self, other: collections.Iterable[OtherItem]
569    ) -> Iter[tuple[Item, OtherItem]]:
570        """Zips the iterator with another iterable.
571
572        Example
573        -------
574        ```py
575        iterator = Iter([1, 2, 3])
576        for item, other_item in iterator.zip([4, 5, 6]):
577            assert item == other_item
578        <Iter([(1, 4), (2, 5), (3, 6)])>
579        ```
580
581        Parameters
582        ----------
583        other: `Iter[OtherItem]`
584            The iterable to zip with.
585
586        Returns
587        -------
588        `Iter[tuple[Item, OtherItem]]`
589            The zipped iterator.
590
591        """
592        return Iter(zip(self.raw_parts(), other))

Zips the iterator with another iterable.

Example
iterator = Iter([1, 2, 3])
for item, other_item in iterator.zip([4, 5, 6]):
    assert item == other_item
<Iter([(1, 4), (2, 5), (3, 6)])>
Parameters
  • other (Iter[OtherItem]): The iterable to zip with.
Returns
  • Iter[tuple[Item, OtherItem]]: The zipped iterator.
def sort( self, *, key: 'collections.Callable[[Item], _typeshed.SupportsRichComparison]', reverse: bool = False) -> Iter[~Item]:
594    def sort(
595        self,
596        *,
597        key: collections.Callable[[Item], _typeshed.SupportsRichComparison],
598        reverse: bool = False,
599    ) -> Iter[Item]:
600        """Sorts the iterator.
601
602        Example
603        -------
604        ```py
605        iterator = Iter([3, 1, 6, 7])
606        for item in iterator.sort(key=lambda item: item < 3):
607            print(item)
608        # 1
609        # 3
610        # 6
611        # 7
612        ```
613
614        Parameters
615        ----------
616        key: `collections.Callable[[Item], Any]`
617            The function to sort by.
618        reverse: `bool`
619            Whether to reverse the sort.
620        """
621        return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))

Sorts the iterator.

Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.sort(key=lambda item: item < 3):
    print(item)
# 1
# 3
# 6
# 7
Parameters
  • key (collections.Callable[[Item], Any]): The function to sort by.
  • reverse (bool): Whether to reverse the sort.
def reversed(self) -> Iter[~Item]:
623    def reversed(self) -> Iter[Item]:
624        """Returns a new iterator that yields the items in the iterator in reverse order.
625
626        This consumes this iterator into a sequence and return a new iterator containing all of the elements
627        in reversed order.
628
629        Example
630        -------
631        ```py
632        iterator = Iter([3, 1, 6, 7])
633        for item in iterator.reversed():
634            print(item)
635        # 7
636        # 6
637        # 1
638        # 3
639        ```
640        """
641        # NOTE: In order to reverse the iterator we need to
642        # first collect it into some collection.
643        return Iter(reversed(list(_ 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]:
645    def union(self, other: collections.Iterable[Item]) -> Iter[Item]:
646        """Returns a new iterator that yields all items from both iterators.
647
648        Example
649        -------
650        ```py
651        iterator = Iter([1, 2, 3])
652        other = [4, 5, 6]
653
654        for item in iterator.union(other):
655            print(item)
656        # 1
657        # 2
658        # 3
659        # 4
660        # 5
661        # 6
662        ```
663
664        Parameters
665        ----------
666        other: `Iter[Item]`
667            The iterable to union with.
668        """
669        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]':
671    def first(self) -> Option[Item]:
672        """Returns the first item in the iterator.
673
674        Example
675        -------
676        ```py
677        iterator = Iter([3, 1, 6, 7])
678        iterator.first().is_some_and(lambda x: x == 3)
679        ```
680        """
681        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]':
683    def last(self) -> Option[Item]:
684        """Returns the last item in the iterator.
685
686        Example
687        -------
688        ```py
689        iterator = Iter([3, 1, 6, 7])
690        iterator.last().is_some_and(lambda x: x == 7)
691        ```
692        """
693        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:
695    def count(self) -> int:
696        """Return the count of elements in memory this iterator has.
697
698        Example
699        -------
700        ```py
701        it = Iter(range(3))
702        assert it.count() == 3
703        ```
704        """
705        count = 0
706        for _ in self:
707            count += 1
708
709        return count

Return the count of elements in memory this iterator has.

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

A place holder that indicates unfinished code.

Example
from sain import todo

def from_json(payload: dict[str, int]) -> int:
    # Calling this function will raise `Error`.
    todo()
Parameters
  • message (str | None): Multiple optional arguments to pass if the error was raised.
  • # Implementations
  • **This function implements todo:
@rustc_diagnostic_item('deprecated')
def deprecated( *, 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]]':
577@rustc_diagnostic_item("deprecated")
578def deprecated(
579    *,
580    obj: collections.Callable[P, U] | None = None,
581    since: typing.Literal["CURRENT_VERSION"] | LiteralString | None = None,
582    removed_in: LiteralString | None = None,
583    use_instead: LiteralString | None = None,
584    hint: LiteralString | None = None,
585) -> (
586    collections.Callable[P, U]
587    | collections.Callable[
588        [collections.Callable[P, U]],
589        collections.Callable[P, U],
590    ]
591):
592    """A decorator that marks a function as deprecated.
593
594    An attempt to call the object that's marked will cause a runtime warn.
595
596    Example
597    -------
598    ```py
599    from sain import deprecated
600
601    @deprecated(
602        since = "1.0.0",
603        removed_in ="3.0.0",
604        use_instead = "UserImpl()",
605        hint = "Hint for ux."
606    )
607    class User:
608        # calling the decorator is not necessary.
609        @deprecated
610        def username(self) -> str:
611            ...
612
613    user = User() # This will cause a warning at runtime.
614
615    ```
616
617    Parameters
618    ----------
619    since : `str`
620        The version that the function was deprecated. the `CURRENT_VERSION` is used internally only.
621    removed_in : `str | None`
622        If provided, It will log when will the object will be removed in.
623    use_instead : `str | None`
624        If provided, This should be the alternative object name that should be used instead.
625    hint: `str`
626        An optional hint for the user.
627    """
628
629    def _create_message(
630        f: typing.Any,
631    ) -> str:
632        msg = f"{_obj_type(f)} `{f.__module__}.{f.__name__}` is deprecated."
633
634        if since is not None:
635            if since == "CURRENT_VERSION":
636                from ._misc import __version__
637
638                msg += " since " + __version__
639            else:
640                msg += " since " + since
641
642        if removed_in:
643            msg += f" Scheduled for removal in `{removed_in}`."
644
645        if use_instead is not None:
646            msg += f" Use `{use_instead}` instead."
647
648        if hint:
649            msg += f" Hint: {hint}"
650        return msg
651
652    def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]:
653        message = _create_message(func)
654
655        @functools.wraps(func)
656        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
657            _warn("\033[93m" + message + "\033[0m", warn_ty=DeprecationWarning)
658            return func(*args, **kwargs)
659
660        # idk why pyright doesn't know the type of wrapper.
661        m = f"\n# Warning ⚠️\n{message}."
662        if wrapper.__doc__:
663            # append this message to an existing document.
664            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}"
665        else:
666            wrapper.__doc__ = m
667
668        return wrapper
669
670    # marked only.
671    if obj is not None:
672        return decorator(obj)
673
674    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:
@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]]':
720@rustc_diagnostic_item("unimplemented")
721def unimplemented(
722    *,
723    obj: collections.Callable[P, U] | None = None,
724    message: LiteralString | None = None,
725    available_in: LiteralString | None = None,
726) -> (
727    collections.Callable[P, U]
728    | collections.Callable[
729        [collections.Callable[P, U]],
730        collections.Callable[P, U],
731    ]
732):
733    """A decorator that marks an object as unimplemented.
734
735    An attempt to call the object that's marked will cause a runtime warn.
736
737    Example
738    -------
739    ```py
740    from sain import unimplemented
741
742    @unimplemented  # Can be used without calling
743    class User:
744        ...
745
746    @unimplemented(message="Not ready", available_in="2.0.0")  # Or with parameters
747    class Config:
748        ...
749    ```
750
751    Parameters
752    ----------
753    message : `str | None`
754        An optional message to be displayed when the function is called. Otherwise default message will be used.
755    available_in : `str | None`
756        If provided, This will be shown as what release this object be implemented.
757    """
758
759    def _create_message(f: typing.Any) -> str:
760        msg = (
761            message
762            or f"{_obj_type(f)} `{f.__module__}.{f.__name__}` is not yet implemented."
763        )
764
765        if available_in:
766            msg += f" Available in `{available_in}`."
767        return msg
768
769    def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]:
770        msg = _create_message(func)
771
772        @functools.wraps(func)
773        def wrapper(*args: P.args, **kwargs: P.kwargs) -> U:
774            _warn("\033[93m" + msg + "\033[0m", warn_ty=RuntimeWarning)
775            return func(*args, **kwargs)
776
777        m = f"\n# Warning ⚠️\n{msg}."
778        if wrapper.__doc__:
779            # Append the new documentation string to the existing docstring.
780            wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + m
781        else:
782            # Assign the new documentation string as the docstring when no existing docstring is present.
783            wrapper.__doc__ = m
784        return wrapper
785
786    if obj is not None:
787        return decorator(obj)
788
789    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:
@rustc_diagnostic_item('doc')
def doc( path: 'Read') -> 'collections.Callable[[collections.Callable[P, U]], collections.Callable[P, U]]':
792@rustc_diagnostic_item("doc")
793def doc(
794    path: Read,
795) -> collections.Callable[
796    [collections.Callable[P, U]],
797    collections.Callable[P, U],
798]:
799    """Set `path` to be the object's documentation.
800
801    Example
802    -------
803    ```py
804    from sain import doc
805    from pathlib import Path
806
807    @doc(Path("../README.md"))
808    class builtins:
809        @doc("bool.html")
810        def bool_docs() -> None:
811            ...
812    ```
813
814    Parameters
815    ----------
816    path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]`
817        The path to read the content from.
818    """
819
820    def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]:
821        with open(path, "r") as file:
822            f.__doc__ = file.read()
823
824        return lambda *args, **kwargs: f(*args, **kwargs)
825
826    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:
@rustc_diagnostic_item('include_str')
def include_str(file: 'LiteralString') -> 'LiteralString':
480@rustc_diagnostic_item("include_str")
481def include_str(file: LiteralString) -> LiteralString:
482    """Includes a file as literal `str`.
483
484    This function is not magic, It is literally defined as
485
486    ```py
487    with open(file, "r") as f:
488        return f.read()
489    ```
490
491    The file name can may be either a relative to the current file or a complete path.
492
493    Example
494    -------
495    ```py
496    from sain.macros import include_str
497
498    def entry() -> None:
499        ...
500
501    entry.__doc__ = include_str("README.md")
502
503    ```
504    """
505    with open(file, "r") as buf:
506        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:
450@rustc_diagnostic_item("include_bytes")
451def include_bytes(file: LiteralString) -> bytes:
452    """Includes a file as `bytes`.
453
454    This function is not magic, It is literally defined as
455
456    ```py
457    with open(file, "rb") as f:
458        return f.read()
459    ```
460
461    The file name can may be either a relative to the current file or a complete path.
462
463    Example
464    -------
465    File "spanish.in":
466    ```text
467    adiós
468    ```
469    File "main.py":
470    ```py
471    from sain.macros import include_bytes
472    buffer = include_bytes("spanish.in")
473    assert buffer.decode() == "adiós"
474    ```
475    """
476    with open(file, "rb") as buf:
477        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:
410@rustc_diagnostic_item("assert_eq")
411def assert_eq(left: T, right: T) -> None:
412    """Asserts that two expressions are equal to each other.
413
414    This exactly as `assert left == right`, but includes a useful message in case of failure.
415
416    Example
417    -------
418    ```py
419    from sain.macros import assert_eq
420    a = 3
421    b = 1 + 2
422    assert_eq(a, b)
423    ```
424    """
425    assert left == right, (
426        f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"'
427    )

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:
430@rustc_diagnostic_item("assert_ne")
431def assert_ne(left: T, right: T) -> None:
432    """Asserts that two expressions are not equal to each other.
433
434    This exactly as `assert left == right`, but includes a useful message in case of failure.
435
436    Example
437    -------
438    ```py
439    from sain.macros import assert_ne
440    a = 3
441    b = 2 + 2
442    assert_ne(a, b)
443    ```
444    """
445    assert left != right, (
446        f'assertion `left != right` failed\nleft: "{left!r}"\nright: "{right!r}"'
447    )

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

A contiguous growable alternative to 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

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

from sain.collections import vec, Vec

# Copy the list 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)
170    def __init__(self, iterable: collections.Iterable[T] | None = None) -> None:
171        """Create an empty `Vec<T>`.
172
173        The vector will not allocate until elements are pushed onto it.
174
175        Example
176        ------
177        ```py
178        vec: Vec[float] = Vec()
179        ```
180        """
181        # We won't allocate to build the list here.
182        # Instead, On first push or fist indexed set
183        # we allocate if it was None.
184        if isinstance(iterable, list):
185            # Calling `list()` on another list will copy it, So instead we just point to it.
186            self._ptr = iterable
187        elif isinstance(iterable, Vec):
188            self._ptr = iterable._ptr
189        # any other iterable that ain't a list needs to get copied into a new list.
190        else:
191            self._ptr: list[T] | None = list(iterable) if iterable else None
192
193        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]:
195    @classmethod
196    def with_capacity(cls, capacity: int) -> Vec[T]:
197        """Create a new `Vec` with at least the specified capacity.
198        This vec will be able to hold `capacity` elements without pushing further.
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.

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            socket.read_to_end(buf.as_mut())
249
250        buf: Vec[int] = Vec()
251        read_bytes(buf.as_mut()) # or just `buf`
252        assert vec.len() >= 1
253        ```
254        """
255        return SliceMut(self)

Return a mutable view over this vector elements.

No copying occurs.

Example
def read_bytes(buf: SliceMut[int]) -> None:
    socket.read_to_end(buf.as_mut())

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

Returns an iterator over this vector elements.

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

Returns true if the vector contains no elements.

Inlined into:

not self.vec
def leak(self) -> list[~T]:
312    def leak(self) -> list[T]:
313        """Consumes and leaks the Vec, returning a mutable reference to the contents.
314
315        After calling this, this vec will no longer reference the underlying buffer,
316        therefore, it becomes unusable.
317
318        if `self` is uninitialized, an empty list is returned.
319
320        This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without
321        any copies.
322
323        Example
324        -------
325        ```py
326        x = Vec([1, 2, 3])
327        owned = x.leak()
328        # x is now unusable.
329        owned[0] += 1
330        assert owned == [2, 2, 3]
331        ```
332        """
333        if self._ptr is not None:
334            # don't point to `_ptr` anymore.
335            tmp = self._ptr
336            del self._ptr
337            return tmp
338
339        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]:
341    def split_off(self, at: int) -> Vec[T]:
342        """Split the vector off at the specified position, returning a new
343        vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`.
344
345        if this vec is empty, `self` is returned unchanged.
346
347        Example
348        -------
349        ```py
350        origin = Vec((1, 2, 3, 4))
351        split = vec.split_off(2)
352
353        print(origin, split)  # [1, 2], [3, 4]
354        ```
355
356        Raises
357        ------
358        `IndexError`
359            This method will raise if `at` > `len(self)`
360        """
361        len_ = self.len()
362        if at > len_:
363            raise IndexError(
364                f"Index `at` ({at}) should be <= than len of vector ({len_}) "
365            ) from None
366
367        # Either the list is empty or uninit.
368        if not self._ptr:
369            return self
370
371        split = self[at:len_]  # split the items into a new vec.
372        del self._ptr[at:len_]  # remove the items from the original list.
373        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]]]':
375    def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
376        """Split the first and rest elements of the vector, If empty, `None` is returned.
377
378        Example
379        -------
380        ```py
381        vec = Vec([1, 2, 3])
382        split = vec.split_first()
383        assert split == Some((1, [2, 3]))
384
385        vec: Vec[int] = Vec()
386        split = vec.split_first()
387        assert split.is_none()
388        ```
389        """
390        if not self._ptr:
391            return _option.NOTHING
392
393        # optimized to only one element in the vector.
394        if self.len() == 1:
395            return _option.Some((self[0], ()))
396
397        first, *rest = self._ptr
398        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]]]':
400    def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]:
401        """Split the last and rest elements of the vector, If empty, `None` is returned.
402
403        Example
404        -------
405        ```py
406        vec = Vec([1, 2, 3])
407        last, rest = vec.split_last().unwrap()
408        assert (last, rest) == [3, [1, 2]]
409        ```
410        """
411        if not self._ptr:
412            return _option.NOTHING
413
414        # optimized to only one element in the vector.
415        if self.len() == 1:
416            return _option.Some((self[0], ()))
417
418        last, *rest = self._ptr[-1], *self._ptr[:-1]
419        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]]:
421    def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]:
422        """Divide `self` into two at an index.
423
424        The first will contain all elements from `[0:mid]` excluding `mid` it self.
425        and the second will contain the remaining elements.
426
427        if `mid` > `self.len()`, Then all elements will be moved to the left,
428        returning an empty vec in right.
429
430        Example
431        -------
432        ```py
433        buffer = Vec((1, 2, 3, 4))
434        left, right = buffer.split_at(0)
435        assert left == [] and right == [1, 2, 3, 4]
436
437        left, right = buffer.split_at(2)
438        assert left == [1, 2] and right == [2, 3]
439        ```
440
441        The is roughly the implementation
442        ```py
443        vec[0:mid], vec[mid:]
444        ```
445        """
446        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):
448    def swap(self, a: int, b: int):
449        """Swap two elements in the vec.
450
451        if `a` equals to `b` then it's guaranteed that elements won't change value.
452
453        Example
454        -------
455        ```py
456        buf = Vec([1, 2, 3, 4])
457        buf.swap(0, 3)
458        assert buf == [4, 2, 3, 1]
459        ```
460
461        Raises
462        ------
463        IndexError
464            If the positions of `a` or `b` are out of index.
465        """
466        if self[a] == self[b]:
467            return
468
469        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):
471    def swap_unchecked(self, a: int, b: int):
472        """Swap two elements in the vec. without checking if `a` == `b`.
473
474        If you care about `a` and `b` equality, see `Vec.swap`.
475
476        Example
477        -------
478        ```py
479        buf = Vec([1, 2, 3, 1])
480        buf.swap_unchecked(0, 3)
481        assert buf == [1, 2, 3, 1]
482        ```
483
484        Raises
485        ------
486        IndexError
487            If the positions of `a` or `b` are out of index.
488        """
489        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]':
491    def first(self) -> _option.Option[T]:
492        """Get the first element in this vec, returning `None` if there's none.
493
494        Example
495        -------
496        ```py
497        vec = Vec((1,2,3))
498        first = vec.first()
499        assert ~first == 1
500        ```
501        """
502        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]':
504    def last(self) -> _option.Option[T]:
505        """Get the last element in this vec, returning `None` if there's none.
506
507        Example
508        -------
509        ```py
510        vec = Vec([1, 2, 3, 4])
511        first = vec.last()
512        assert ~first == 4
513        ```
514        """
515        return self.get(-1)

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

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

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

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

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

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

Fill self with the given value.

Nothing happens if the vec is empty or unallocated.

Example

a = Vec([0, 1, 2, 3])
a.fill(0)
assert a == [0, 0, 0, 0]
def push(self, item: ~T) -> None:
603    def push(self, item: T) -> None:
604        """Push an element at the end of the vector.
605
606        Example
607        -------
608        ```py
609        vec = Vec()
610        vec.push(1)
611
612        assert vec == [1]
613        ```
614        """
615        if self._capacity is not None:
616            self.push_within_capacity(item)
617            return
618
619        if self._ptr is None:
620            self._ptr = []
621
622        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]':
624    def push_within_capacity(self, x: T) -> Result[None, T]:
625        """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
626
627        Example
628        -------
629        ```py
630        vec: Vec[int] = Vec.with_capacity(3)
631        for i in range(3):
632            match vec.push_within_capacity(i):
633                case Ok(_):
634                    print("All good.")
635                case Err(elem):
636                    print("Reached max cap :< can't push", elem)
637        ```
638
639        Or you can also just call `Vec.push` and it will push if there's is sufficient capacity.
640        ```py
641        vec: Vec[int] = Vec.with_capacity(3)
642
643        vec.extend((1, 2, 3))
644        vec.push(4)
645
646        assert vec.len() == 3
647        ```
648        """
649        if self._ptr is None:
650            self._ptr = []
651
652        if self.len() == self._capacity:
653            return _result.Err(x)
654
655        self._ptr.append(x)
656        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:
658    def reserve(self, additional: int) -> None:
659        """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>.
660
661        Example
662        -------
663        ```py
664        vec = Vec.with_capacity(3)
665        is_vip = random.choice((True, False))
666
667        for i in range(4):
668            match vec.push_within_capacity(i):
669                case Ok(_):
670                    print("All good")
671                case Err(person):
672                    # If the person is a VIP, then reserve for one more.
673                    if is_vip:
674                        vec.reserve(1)
675                        continue
676
677                    # is_vip was false.
678                    print("Can't reserve for non-VIP members...", person)
679                    break
680        ```
681        """
682        if self._capacity is not None:
683            self._capacity += additional

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

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

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

            # is_vip was false.
            print("Can't reserve for non-VIP members...", person)
            break
def shrink_to_fit(self) -> None:
685    def shrink_to_fit(self) -> None:
686        """Shrink the capacity of this `Vec` to match its length.
687
688        If `self` is initialized with no capacity, This is a `NOP`.
689
690        Example
691        -------
692        ```py
693        s = Vec([1, 2, 3, 4])
694
695        s.reserve(100)
696        s.shrink_to_fit()
697        assert s.capacity() == 4
698        ```
699        """
700        if self._capacity is None:
701            return
702
703        # The capacity is never less than the length.
704        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:
706    def shrink_to(self, min_capacity: int) -> None:
707        """Shrink the capacity of this `Vec` to a minimum specified capacity.
708
709        If `self` is initialized with no capacity or the current capacity is less than the lower limit,
710        This is a `NOP`.
711
712        Example
713        -------
714        ```py
715        vec = Vec.with_capacity(10)
716        vec.extend([1, 2, 3])
717        assert vec.capacity() >= 10
718        vec.shrink_to(4)
719        assert vec.capacity() >= 4
720        vec.shrink_to(0)
721        ```
722        """
723        if self._capacity is None or self._capacity <= min_capacity:
724            return
725
726        # Ensure the capacity is not reduced below the current length of the vector.
727        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:
733    def append(self, value: T) -> None:
734        """An alias to `Vec.push`."""
735        self.push(value)

An alias to Vec.push.

def get(self, index: int) -> '_option.Option[T]':
737    def get(self, index: int) -> _option.Option[T]:
738        """Get the item at the given index, or `Some[None]` if its out of bounds.
739
740        Example
741        -------
742        ```py
743        vec = Vec((1, 2, 3))
744        vec.get(0) == Some(1)
745        vec.get(3) == Some(None)
746        ```
747        """
748        try:
749            return _option.Some(self[index])
750        except IndexError:
751            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:
753    def insert(self, index: int, value: T) -> None:
754        """Insert an element at the position `index`.
755
756        Example
757        --------
758        ```py
759        vec = Vec((2, 3))
760        vec.insert(0, 1)
761        assert vec == [1, 2, 3]
762        ```
763        """
764        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]':
766    def pop(self, index: int = -1) -> _option.Option[T]:
767        """Removes the last element from a vector and returns it, or `None` if it is empty.
768
769        Example
770        -------
771        ```py
772        vec = Vec((1, 2, 3))
773        assert vec.pop() == Some(3)
774        assert vec == [1, 2]
775        ```
776        """
777        if not self._ptr:
778            return _option.NOTHING
779
780        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]':
782    def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]:
783        """Removes the last element from a vector and returns it if `f` returns `True`,
784        or `None` if it is empty.
785
786        Example
787        -------
788        ```py
789        vec = Vec((1, 2, 3))
790        assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
791        assert vec == [1, 2]
792        ```
793        """
794        if not self._ptr:
795            return _option.NOTHING
796
797        if pred(self[-1]):
798            return self.pop()
799
800        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:
802    def dedup(self) -> None:
803        """Removes consecutive repeated elements in the vector according to their `__eq__`
804        implementation.
805
806        Example
807        -------
808        ```py
809        vec = Vec([1, 2, 2, 3, 2])
810        vec.dedup()
811        assert vec == [1, 2, 3, 2]
812        """
813        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:
815    def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None:
816        """Removes all but the first of consecutive elements in the vector satisfying a given equality.
817
818        Example
819        -------
820        ```py
821        vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
822        vec.dedup_by(lambda a, b: a.lower() == b.lower())
823        assert vec == ["foo", "bar", "baz", "bar"]
824        ```
825
826        Only remove consecutive duplicates.
827        ```py
828        vec = Vec([1, 2, 2, 3, 4, 1])
829        vec.dedup_by(lambda a, b: a == b)
830        # The final 1 is not adjacent to the first 1, so it is not removed.
831        assert vec == [1, 2, 3, 4, 1]
832        ```
833        """
834
835        if not self._ptr or (len_ := len(self._ptr)) <= 1:
836            return
837
838        idx = 1
839        while idx < len_:
840            if same_bucket(self._ptr[idx], self._ptr[idx - 1]):
841                del self._ptr[idx]
842                len_ -= 1
843            else:
844                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:
846    def remove(self, item: T) -> None:
847        """Remove the first appearance of `item` from this vector.
848
849        Example
850        -------
851        ```py
852        vec = Vec(('a', 'b', 'c'))
853        vec.remove('a')
854        assert vec == ['b', 'c']
855        ```
856        """
857        if not self._ptr:
858            return
859
860        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:
862    def extend(self, iterable: collections.Iterable[T]) -> None:
863        """Extend this vector from another iterable.
864
865        Example
866        -------
867        ```py
868        vec = Vec((1, 2, 3))
869        vec.extend((4, 5, 6))
870
871        assert vec == [1, 2, 3, 4, 5, 6]
872        ```
873        """
874        if self._ptr is None:
875            self._ptr = []
876
877        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]:
879    def copy(self) -> Vec[T]:
880        """Copy all elements of `self` into a new vector.
881
882        Example
883        -------
884        ```py
885        original = Vec((1,2,3))
886        copy = original.copy()
887        copy.push(4)
888
889        print(original) # [1, 2, 3]
890        ```
891        """
892        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:
894    def clear(self) -> None:
895        """Clear all elements of this vector.
896
897        Example
898        -------
899        ```py
900        vec = Vec((1,2,3))
901        vec.clear()
902        assert vec.len() == 0
903        ```
904        """
905        if not self._ptr:
906            return
907
908        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:
910    def sort(
911        self,
912        *,
913        key: collections.Callable[[T], SupportsRichComparison] | None = None,
914        reverse: bool = False,
915    ) -> None:
916        """This method sorts the list in place, using only < comparisons between items.
917
918        Example
919        -------
920        ```py
921        vec = Vec((2, 1, 3))
922        vec.sort()
923        assert vec == [1, 2, 3]
924        ```
925        """
926        if not self._ptr:
927            return
928
929        # key can be `None` here just fine, idk why pyright is complaining.
930        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:
932    def index(
933        self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize
934    ) -> int:
935        # << Official documentation >>
936        """Return zero-based index in the vec of the first item whose value is equal to `item`.
937        Raises a ValueError if there is no such item.
938
939        Example
940        -------
941        ```py
942        vec = Vec((1, 2, 3))
943        assert vec.index(2) == 1
944        ```
945        """
946        if self._ptr is None:
947            raise ValueError from None
948
949        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:
951    def count(self, item: T) -> int:
952        """Return the number of occurrences of `item` in the vec.
953
954        `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found.
955
956        Example
957        --------
958        ```py
959        vec = Vec((1, 2, 3, 3))
960        assert vec.count(3) == 2
961        ```
962        """
963        if self._ptr is None:
964            return 0
965
966        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'