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
andSome
variant,Result
andOk
,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
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:
1# BSD 3-Clause License 2# 3# Copyright (c) 2022-Present, nxtlo 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31""" 32.. include:: ../DOCS.md 33""" 34 35from __future__ import annotations 36 37__all__ = ( 38 # async_iter.py 39 "async_iter", 40 # cfg.py 41 "cfg", 42 "cfg_attr", 43 # default.py 44 "default", 45 "Default", 46 # option.py 47 "option", 48 "Some", 49 "Option", 50 "NOTHING", 51 # iter.py 52 "Iter", 53 "Iterator", 54 "iter", 55 # macros.py 56 "macros", 57 "todo", 58 "deprecated", 59 "unimplemented", 60 "doc", 61 "include_str", 62 "include_bytes", 63 "assert_eq", 64 "assert_ne", 65 # futures.py 66 "futures", 67 # result.py 68 "result", 69 "Ok", 70 "Err", 71 "Result", 72 # collections 73 "collections", 74 "Vec", 75 # error.py 76 "error", 77 "Error", 78 # boxed.py 79 "boxed", 80 "Box", 81 # sync 82 "sync", 83 # maybe_uninit.py 84 "maybe_uninit", 85 # convert 86 "convert", 87 "From", 88 "TryFrom", 89 "Into", 90 "TryInto", 91 "ToString", 92 # time 93 "time", 94 # misc 95 "__version__", 96 "__url__", 97 "__author__", 98 "__about__", 99 "__license__", 100) 101 102from . import async_iter 103from . import boxed 104from . import collections 105from . import convert 106from . import default 107from . import error 108from . import futures 109from . import iter 110from . import macros 111from . import maybe_uninit 112from . import option 113from . import result 114from . import sync 115from . import time 116from ._misc import __about__ 117from ._misc import __author__ 118from ._misc import __license__ 119from ._misc import __url__ 120from ._misc import __version__ 121from .boxed import Box 122from .cfg import cfg 123from .cfg import cfg_attr 124from .collections import Vec 125from .convert import From 126from .convert import Into 127from .convert import ToString 128from .convert import TryFrom 129from .convert import TryInto 130from .default import Default 131from .error import Error 132from .iter import Iter 133from .iter import Iterator 134from .macros import assert_eq 135from .macros import assert_ne 136from .macros import deprecated 137from .macros import doc 138from .macros import include_bytes 139from .macros import include_str 140from .macros import todo 141from .macros import unimplemented 142from .option import NOTHING 143from .option import Option 144from .option import Some 145from .result import Err 146from .result import Ok 147from .result import Result
174@rustc_diagnostic_item("cfg") 175def cfg( 176 target_os: System | None = None, 177 python_version: tuple[int, ...] | None = None, 178 target_arch: Arch | None = None, 179 impl: Python | None = None, 180) -> bool: 181 """A function that will run the code only if all predicate attributes returns `True`. 182 183 The difference between this function and `cfg_attr` is that this function will not raise an exception. 184 Instead it will return `False` if any of the attributes fails. 185 186 Example 187 ------- 188 ```py 189 import sain 190 191 if cfg(target_os="windows"): 192 print("Windows") 193 elif cfg(target_os="linux", target_arch="arm64"): 194 print("Linux") 195 else: 196 print("Something else") 197 ``` 198 199 Parameters 200 ---------- 201 target_os : `str | None` 202 The targeted operating system that's required for the object to be executed. 203 python_version : `tuple[int, ...] | None` 204 The targeted Python version that's required for the object to be executed. Format must be `(3, ..., ...)` 205 target_arch : `str | None` 206 The CPU targeted architecture that's required for the object to be executed. 207 impl : `str | None` 208 The Python implementation that's required for the object to be executed. 209 210 Returns 211 ------- 212 `bool` 213 The condition that was checked. 214 """ 215 checker = _AttrCheck( 216 lambda: None, 217 no_raise=True, 218 target_os=target_os, 219 python_version=python_version, 220 target_arch=target_arch, 221 impl=impl, 222 ) 223 return checker.check_once()
A function that will run the code only if all predicate attributes returns True
.
The difference between this function and cfg_attr
is that this function will not raise an exception.
Instead it will return False
if any of the attributes fails.
Example
import sain
if cfg(target_os="windows"):
print("Windows")
elif cfg(target_os="linux", target_arch="arm64"):
print("Linux")
else:
print("Something else")
Parameters
- target_os (
str | None
): The targeted operating system that's required for the object to be executed. - python_version (
tuple[int, ...] | None
): The targeted Python version that's required for the object to be executed. Format must be(3, ..., ...)
- target_arch (
str | None
): The CPU targeted architecture that's required for the object to be executed. - impl (
str | None
): The Python implementation that's required for the object to be executed.
Returns
bool
: The condition that was checked.
Implementations
This function implements cfg in Rust.
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 returnsFalse
.ValueError
: If the passed Python implementation is unknown.
Implementations
This function implements cfg_attr in Rust.
59@rustc_diagnostic_item("Default") 60@typing.runtime_checkable 61class Default(typing.Protocol[_T_co]): 62 """An interface for an object that has a default value. 63 64 Example 65 ------- 66 ```py 67 from sain import Default 68 69 class Cache(Default[dict[str, Any]]): 70 71 @staticmethod 72 def default() -> dict[str, Any]: 73 return {} 74 75 cache = Cache.default() 76 print(cache) 77 assert isinstance(cache, Default) 78 # {} 79 ``` 80 """ 81 82 __slots__ = () 83 84 # FIXME: `impl Default for String` knows the type of `Self` is `String` but we can't do that. 85 # So generics is the only way to achieve the same effect. But `Default` in Rust is not generic. 86 # in the future we just swap to `Self`. 87 @staticmethod 88 @rustc_diagnostic_item("default_fn") 89 def default() -> _T_co: 90 """Return the default value of the object.""" 91 raise NotImplementedError
An interface for an object that has a default value.
Example
from sain import Default
class Cache(Default[dict[str, Any]]):
@staticmethod
def default() -> dict[str, Any]:
return {}
cache = Cache.default()
print(cache)
assert isinstance(cache, Default)
# {}
Implementations
This class implements Default in Rust.
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)
87 @staticmethod 88 @rustc_diagnostic_item("default_fn") 89 def default() -> _T_co: 90 """Return the default value of the object.""" 91 raise NotImplementedError
Return the default value of the object.
Implementations
This function implements .Default.html#tymethodsain.default">default_fn in Rust.
56@rustc_diagnostic_item("Option") 57@typing.final 58class Some( 59 typing.Generic[T], 60 _default.Default["Option[T]"], 61): 62 """The `Option` type represents optional value, higher-level abstraction over the `None` type. 63 64 It combines union of `T | None` in one convenient structure, allowing the users to manipulate and propagate 65 the contained value idiomatically. 66 67 An `Option` value have multiple use cases: 68 69 * Initial values. 70 * Return value for functions that may or may not contain a return value. 71 * Optional parameters, class fields. 72 * Swapping values. 73 74 Example 75 ------- 76 ```py 77 # the actual implementation of the object. 78 from sain import Some 79 # Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like. 80 from sain import Option 81 82 def divide(numerator: float, denominator: float) -> Option[float]: 83 if denominator == 0.0: 84 return Some(None) 85 return Some(numerator / denominator) 86 87 # Returns Option[float] 88 result = divide(2.0, 3.0) 89 90 # Pattern match to retrieve the value 91 match result: 92 # The division is valid. 93 case Some(x): 94 print("Result:", x) 95 # Invalid division, this is Some(None) 96 case _: 97 print("cannot divide by 0") 98 ``` 99 100 ### Converting `None`s into `RuntimeError`s 101 102 Sometimes we want to extract the value and cause an error to the caller if it doesn't exist, 103 104 because handling `Some/None` can be tedious, luckily we have several ways to deal with this. 105 106 ```py 107 def ipaddr(s: str) -> Option[tuple[int, int, int, int]]: 108 match s.split('.'): 109 case [a, b, c, d]: 110 return Some((int(a), int(b), int(c), int(d))) 111 case _: 112 return Some(None) 113 114 # calls `unwrap()` for you. 115 ip = ~ipaddr("192.168.1.19") 116 # causes a `RuntimeError` if it returns `None`. 117 ip = ipaddr("192.168.1.19").unwrap() 118 # causes a `RuntimeError` if it returns `None`, with a context message. 119 ip = ipaddr("192.168.1.19").expect("i need an ip address :<") 120 ``` 121 122 The `~` operator will result in `tuple[int, int, int, int]` if the parsing succeed. 123 unless the contained value is `None`, it will cause a `RuntimeError`. 124 125 If the value must be present, use `unwrap_or`, which takes a default parameter 126 and returns it in-case `ipaddr` returns `None` 127 ```py 128 ip = ipaddr("blah blah blah").unwrap_or("192.168.1.255") 129 # Results: 192.168.1.255 130 ``` 131 132 Overall, this type provides many other functional methods such as `map`, `filter`. 133 134 boolean checks such as `is_some`, `is_none`, converting between `Option` and `Result` using `ok_or`, and many more. 135 """ 136 137 __slots__ = ("_value",) 138 __match_args__ = ("_value",) 139 140 def __init__(self, value: T | None, /) -> None: 141 self._value = value 142 143 @staticmethod 144 def default() -> Option[T]: 145 """Default value for `Option<T>`. Returns `None` wrapped in `Some`. 146 147 Example 148 ------- 149 ```py 150 assert Some[int].default() is NOTHING 151 ``` 152 """ 153 return NOTHING 154 155 # *- Reading the value -* 156 157 def transpose(self) -> T | None: 158 """Convert `Option[T]` into `T | None`. 159 160 Examples 161 -------- 162 ```py 163 opt = Some('char') 164 x = opt.transpose() 165 assert x == 'char' 166 167 opt = Some(None) 168 assert opt.transpose() is None 169 ``` 170 """ 171 return self._value 172 173 def unwrap(self) -> T: 174 """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`. 175 176 It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns. 177 178 Example 179 ------- 180 ```py 181 value = Some(5) 182 print(value.unwrap()) 183 # 5 184 185 value = Some(None) 186 print(value.unwrap()) 187 # RuntimeError 188 ``` 189 190 Raises 191 ------ 192 `RuntimeError` 193 If the inner value is `None`. 194 """ 195 if self._value is None: 196 raise RuntimeError("Called `Option.unwrap()` on `None`.") from None 197 198 return self._value 199 200 def unwrap_or(self, default: T, /) -> T: 201 """Unwrap the inner value either returning if its not `None` or returning `default`. 202 203 Example 204 ------- 205 ```py 206 value = Some(5) 207 print(value.unwrap_or(10)) 208 # 5 209 210 # Type hint is required here. 211 value: Option[int] = Some(None) 212 print(value.unwrap_or(10)) 213 # 10 214 ``` 215 """ 216 if self._value is None: 217 return default 218 219 return self._value 220 221 def unwrap_or_else(self, f: FnOnce[T], /) -> T: 222 """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value. 223 224 Example 225 ------- 226 ```py 227 value = Some(5) 228 print(value.unwrap_or_else(lambda: 10)) 229 # 5 230 231 value: Option[bool] = Some(None) 232 print(value.unwrap_or_else(lambda: True)) 233 # True 234 ``` 235 """ 236 if self._value is None: 237 return f() 238 239 return self._value 240 241 @macros.unsafe 242 def unwrap_unchecked(self) -> T: 243 """Returns the contained Some value without checking that the value is not None. 244 245 Example 246 ------- 247 ```py 248 v: Option[float] = Some(1.2) 249 v.unwrap_unchecked() # 1.2 250 251 v: Option[float] = Some(None) 252 print(v.unwrap_unchecked()) # Undefined Behavior 253 ``` 254 """ 255 #! SAFETY: The caller guarantees that the value is not None. 256 return self._value # pyright: ignore 257 258 def expect(self, message: str, /) -> T: 259 """Returns the contained `Some` value. 260 261 raises if the value is `None` with a custom provided `message`. 262 263 Example 264 ------- 265 ```py 266 value = Some("Hello") 267 268 print(value.expect("Value is None")) 269 # "Hello" 270 271 value: Option[str] = Some(None) 272 print(value.expect("Value is None")) 273 # RuntimeError("Value is None") 274 ``` 275 """ 276 if self._value is None: 277 raise RuntimeError(message) 278 279 return self._value 280 281 # *- object transformation -* 282 283 def map(self, f: Fn[T, U], /) -> Option[U]: 284 """Map the inner value to another type. Returning `Some(None)` if `T` is `None`. 285 286 Example 287 ------- 288 ```py 289 value = Some(5.0) 290 291 print(value.map(lambda x: x * 2.0)) 292 # Some(10.0) 293 294 value: Option[bool] = Some(None) 295 print(value) 296 # Some(None) 297 ``` 298 """ 299 if self._value is None: 300 return NOTHING 301 302 return Some(f(self._value)) 303 304 def map_or(self, default: U, f: Fn[T, U], /) -> U: 305 """Map the inner value to another type or return `default` if its `None`. 306 307 Example 308 ------- 309 ```py 310 value: Option[float] = Some(5.0) 311 312 # map to int. 313 print(value.map_or(0, int)) 314 # 6 315 316 value: Option[float] = Some(None) 317 print(value.map_or(0, int) 318 # 0 319 ``` 320 """ 321 if self._value is None: 322 return default 323 324 return f(self._value) 325 326 def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U: 327 """Map the inner value to another type, or return `default()` if its `None`. 328 329 Example 330 ------- 331 ```py 332 def default() -> int: 333 return sys.getsizeof(object()) 334 335 value: Option[float] = Some(5.0) 336 337 # map to int. 338 print(value.map_or_else(default, int)) 339 # 6 340 341 value: Option[float] = Some(None) 342 print(value.map_or_else(default, int) 343 # 28 <- size of object() 344 ``` 345 """ 346 if self._value is None: 347 return default() 348 349 return f(self._value) 350 351 def filter(self, predicate: Fn[T, bool]) -> Option[T]: 352 """Returns `Some(None)` if the contained value is `None`, 353 354 otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`. 355 356 Example 357 ------- 358 ```py 359 value = Some([1, 2, 3]) 360 361 print(value.filter(lambda x: 1 in x)) 362 # Some([1, 2, 3]) 363 364 value: Option[int] = Some([1, 2, 3]) # or Some(None) 365 print(value.filter(lambda x: 1 not in x)) 366 # None 367 ``` 368 """ 369 if (value := self._value) is not None: 370 if predicate(value): 371 return Some(value) 372 373 return NOTHING 374 375 def ok_or(self, err: U) -> _result.Result[T, U]: 376 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. 377 378 Example 379 ------- 380 ```py 381 xyz: Option[str] = Some("foo") 382 assert xyz.ok_or(None) == Ok("foo") 383 384 xyz: Option[str] = Some(None) 385 assert xyz.ok_or(None) == Err(None) 386 ``` 387 """ 388 if self._value is None: 389 return _result.Err(err) 390 391 return _result.Ok(self._value) 392 393 def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]: 394 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. 395 396 Example 397 ------- 398 ```py 399 xyz: Option[str] = Some("foo") 400 assert xyz.ok_or(None) == Ok("foo") 401 402 xyz: Option[str] = Some(None) 403 assert xyz.ok_or(None) == Err(None) 404 ``` 405 """ 406 if self._value is None: 407 return _result.Err(err()) 408 409 return _result.Ok(self._value) 410 411 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 412 """Zips `self` with `other`. 413 414 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`. 415 416 Example 417 ------- 418 ```py 419 x = Some(1) 420 y = Some("hi") 421 z: Option[str] = Some(None) 422 423 assert x.zip(y) == Some((1, "hi")) 424 assert x.zip(z) == Some(None) 425 ``` 426 """ 427 if self._value is not None and other._value is not None: 428 return Some((self._value, other._value)) 429 430 return NOTHING 431 432 def zip_with( 433 self, other: Option[U], f: collections.Callable[[T, U], T_co] 434 ) -> Option[T_co]: 435 """Zips `self` with `other` using function `f`. 436 437 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`. 438 439 Example 440 ------- 441 ```py 442 @dataclass 443 class Point: 444 x: float 445 y: float 446 447 x, y = Some(32.1), Some(42.4) 448 assert x.zip_with(y, Point) == Some(Point(32.1, 42.4)) 449 ``` 450 """ 451 if self._value is not None and other._value is not None: 452 return Some(f(self._value, other._value)) 453 454 return NOTHING 455 456 # *- Inner operations *- 457 458 def take(self) -> Option[T]: 459 """Take the value from `self` Setting it to `None`, and then return `Some(v)`. 460 461 If you don't care about the original value, use `Option.clear()` instead. 462 463 Example 464 ------- 465 ```py 466 original = Some("Hi") 467 new = original.take() 468 469 print(original, new) 470 # None, Some("Hi") 471 ``` 472 """ 473 if self._value is None: 474 return NOTHING 475 476 val = self._value 477 self._value = None 478 return Some(val) 479 480 def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]: 481 """Take the value from `Self`, Setting it to `None` only if predicate returns `True`. 482 483 If you don't care about the original value, use `Option.clear_if()` instead. 484 485 Example 486 ------- 487 ```py 488 def validate(email: str) -> bool: 489 # you can obviously validate this better. 490 return email.find('@') == 1 491 492 original = Some("flex@gg.com") 493 valid = original.take_if(validate) 494 assert is_allowed.is_some() and original.is_none() 495 496 original = Some("mail.example.com") 497 invalid = original.take_if(validate) 498 assert invalid.is_none() and original.is_some() 499 ``` 500 """ 501 if self.map_or(False, predicate): 502 return self.take() 503 504 return NOTHING 505 506 def clear(self) -> None: 507 """Clear the inner value, setting it to `None`. 508 509 If you care about the original value, use `Option.take()` instead. 510 511 Example 512 ------- 513 ```py 514 value = Some("Hello") 515 value.clear() 516 assert value.is_none() 517 ``` 518 """ 519 self._value = None 520 521 def clear_if(self, predicate: Fn[T, bool]) -> None: 522 """Clear the inner value, setting it to `None` if the predicate returns `True`. 523 524 If you care about the original value, use `Option.take_if()` instead. 525 526 Example 527 ------- 528 ```py 529 value = Some("Hello") 530 value.clear_if(lambda x: x == "Hello") 531 assert value.is_none() 532 ``` 533 """ 534 if self._value is not None and predicate(self._value): 535 self._value = None 536 537 def replace(self, value: T) -> Option[T]: 538 """Replace the contained value with another value. 539 540 Use `Option.insert` if you want to return the original value 541 that got inserted instead of `self` 542 543 Example 544 ------- 545 ```py 546 value: Option[str] = Some(None) 547 value.replace("Hello") 548 # Some("Hello") 549 ``` 550 """ 551 self._value = value 552 return self 553 554 def insert(self, value: T) -> T: 555 """Insert a value into the option, and then return a reference to it. 556 557 This will overwrite the old value if it was already contained. 558 559 Example 560 ------- 561 ```py 562 flag: Option[bool] = Some(None) 563 flag_ref = flag.insert(True) 564 assert flag_ref == True 565 assert flag.unwrap() == True 566 ``` 567 """ 568 self._value = value 569 return value 570 571 def get_or_insert(self, value: T) -> T: 572 """Insert a value into the option if it was `None`, 573 and then return a reference to it. 574 575 Example 576 ------- 577 ```py 578 state: Option[bool] = Some(None) 579 assert state.get_or_insert(True) is True 580 assert state.get_or_insert(False) is True 581 ``` 582 """ 583 if self._value is not None: 584 return self._value 585 586 self._value = value 587 return value 588 589 def get_or_insert_with(self, f: FnOnce[T]) -> T: 590 """Insert a value into the option computed from `f()` if it was `None`, 591 and then return a reference to it. 592 593 Example 594 ------- 595 ```py 596 flag: Option[bool] = Some(None) 597 flag_ref = flag.insert(True) 598 assert flag_ref == True 599 assert flag.unwrap() == True 600 ``` 601 """ 602 if self._value is not None: 603 return self._value 604 605 v = self._value = f() 606 return v 607 608 def and_ok(self, optb: Option[T]) -> Option[T]: 609 """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`. 610 611 aliases: `Option::and` 612 613 Example 614 ------- 615 ```py 616 x = Some(1) 617 y: Option[str] = Some(None) 618 assert x.and_ok(y) == Some(None) 619 620 x: Option[str] = Some(None) 621 y = Some(1) 622 assert x.and_ok(y) == Some(None) 623 624 x: Option[str] = Some("hi") 625 y = Some(100) 626 assert x.and_ok(y) == Some(100) 627 ``` 628 """ 629 if self._value is None or optb._value is None: 630 return optb 631 632 return NOTHING 633 634 def and_then(self, f: Fn[T, Option[T]]) -> Option[T]: 635 """Returns `Some(None)` if the contained value is `None`, otherwise call `f()` 636 on `T` and return `Option[T]`. 637 638 Example 639 ------- 640 ```py 641 value = Some(5) 642 print(value.and_then(lambda x: Some(x * 2))) 643 # Some(10) 644 645 value: Option[int] = Some(None) 646 print(value.and_then(lambda x: Some(x * 2))) 647 # Some(None) 648 ``` 649 """ 650 if self._value is None: 651 return NOTHING 652 653 return f(self._value) 654 655 def inspect(self, f: Fn[T, typing.Any]) -> Option[T]: 656 """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing. 657 658 Example 659 ------- 660 ```py 661 def debug(x: str) -> None: 662 print("Debugging:", x) 663 664 value = Some("foo") 665 inner = value.inspect(debug).expect("no value to debug") 666 # prints: Debugging: "foo" 667 668 value: Option[str] = Some(None) 669 value.inspect(debug) # prints nothing 670 """ 671 if self._value is not None: 672 f(self._value) 673 674 return self 675 676 # *- Builder methods *- 677 678 def iter(self) -> _iter.ExactSizeIterator[T]: 679 """Returns an iterator over the contained value. 680 681 Example 682 ------- 683 ```py 684 from sain import Some 685 value = Some("gg").iter() 686 assert value.next() == Some("gg") 687 688 value: Option[int] = Some(None) 689 assert value.iter().next().is_none() 690 ``` 691 """ 692 if self._value is None: 693 return _iter.empty() 694 695 return _iter.once(self._value) 696 697 # *- Boolean checks *- 698 699 def is_some(self) -> bool: 700 """Returns `True` if the contained value is not `None`, otherwise returns `False`. 701 702 Example 703 ------- 704 ```py 705 value = Some(5) 706 print(value.is_some()) 707 # True 708 709 value: Option[int] = Some(None) 710 print(value.is_some()) 711 # False 712 ``` 713 """ 714 return self._value is not None 715 716 def is_some_and(self, predicate: Fn[T, bool]) -> bool: 717 """Returns `True` if the contained value is not `None` and 718 the predicate returns `True`, otherwise returns `False`. 719 720 Example 721 ------- 722 ```py 723 value = Some(5) 724 print(value.is_some_and(lambda x: x > 3)) 725 # True 726 727 value: Option[int] = Some(None) 728 print(value.is_some_and(lambda x: x > 3)) 729 # False 730 ``` 731 """ 732 return self._value is not None and predicate(self._value) 733 734 def is_none(self) -> bool: 735 """Returns `True` if the contained value is `None`, otherwise returns `False`. 736 737 Example 738 ------- 739 ```py 740 value = Some(5) 741 print(value.is_none()) 742 # False 743 744 value: Option[int] = Some(None) 745 print(value.is_none()) 746 # True 747 ``` 748 """ 749 return self._value is None 750 751 def is_none_or(self, f: Fn[T, bool]) -> bool: 752 """Returns `True` if the contained value is `None` or the predicate returns `True`, 753 otherwise returns `False`. 754 755 Example 756 ------- 757 ```py 758 value = Some(5) 759 print(value.is_none_or(lambda x: x > 3)) 760 # False 761 762 value: Option[int] = Some(None) 763 print(value.is_none_or(lambda x: x > 3)) 764 # True 765 ``` 766 """ 767 match self._value: 768 case None: 769 return True 770 case x: 771 return f(x) 772 773 def __repr__(self) -> str: 774 if self._value is None: 775 return "None" 776 return f"Some({self._value!r})" 777 778 __str__ = __repr__ 779 780 def __invert__(self) -> T: 781 return self.unwrap() 782 783 def __or__(self, other: T) -> T: 784 return self._value if self._value is not None else other 785 786 def __bool__(self) -> bool: 787 return self._value is not None 788 789 def __eq__(self, other: None | object) -> bool: 790 if other is None: 791 return self._value is None 792 793 if not isinstance(other, Some): 794 return NotImplemented 795 796 return self._value == other._value # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType] 797 798 def __ne__(self, other: object) -> bool: 799 return not self.__eq__(other) 800 801 def __hash__(self) -> int: 802 return hash(self._value)
The Option
type represents optional value, higher-level abstraction over the None
type.
It combines union of T | None
in one convenient structure, allowing the users to manipulate and propagate
the contained value idiomatically.
An Option
value have multiple use cases:
- Initial values.
- Return value for functions that may or may not contain a return value.
- Optional parameters, class fields.
- Swapping values.
Example
# the actual implementation of the object.
from sain import Some
# Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like.
from sain import Option
def divide(numerator: float, denominator: float) -> Option[float]:
if denominator == 0.0:
return Some(None)
return Some(numerator / denominator)
# Returns Option[float]
result = divide(2.0, 3.0)
# Pattern match to retrieve the value
match result:
# The division is valid.
case Some(x):
print("Result:", x)
# Invalid division, this is Some(None)
case _:
print("cannot divide by 0")
Converting None
s into RuntimeError
s
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.
143 @staticmethod 144 def default() -> Option[T]: 145 """Default value for `Option<T>`. Returns `None` wrapped in `Some`. 146 147 Example 148 ------- 149 ```py 150 assert Some[int].default() is NOTHING 151 ``` 152 """ 153 return NOTHING
Default value for Option<T>
. Returns None
wrapped in Some
.
Example
assert Some[int]sain.default() is NOTHING
157 def transpose(self) -> T | None: 158 """Convert `Option[T]` into `T | None`. 159 160 Examples 161 -------- 162 ```py 163 opt = Some('char') 164 x = opt.transpose() 165 assert x == 'char' 166 167 opt = Some(None) 168 assert opt.transpose() is None 169 ``` 170 """ 171 return self._value
Convert Option[T]
into T | None
.
Examples
opt = Some('char')
x = opt.transpose()
assert x == 'char'
opt = Some(None)
assert opt.transpose() is None
173 def unwrap(self) -> T: 174 """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`. 175 176 It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns. 177 178 Example 179 ------- 180 ```py 181 value = Some(5) 182 print(value.unwrap()) 183 # 5 184 185 value = Some(None) 186 print(value.unwrap()) 187 # RuntimeError 188 ``` 189 190 Raises 191 ------ 192 `RuntimeError` 193 If the inner value is `None`. 194 """ 195 if self._value is None: 196 raise RuntimeError("Called `Option.unwrap()` on `None`.") from None 197 198 return self._value
Unwrap the inner value either returning if its not None
or raising a RuntimeError
.
It's usually not recommended to use this method in production code, and instead use safer options such as unwrap_or
or match patterns.
Example
value = Some(5)
print(value.unwrap())
# 5
value = Some(None)
print(value.unwrap())
# RuntimeError
Raises
RuntimeError
: If the inner value isNone
.
200 def unwrap_or(self, default: T, /) -> T: 201 """Unwrap the inner value either returning if its not `None` or returning `default`. 202 203 Example 204 ------- 205 ```py 206 value = Some(5) 207 print(value.unwrap_or(10)) 208 # 5 209 210 # Type hint is required here. 211 value: Option[int] = Some(None) 212 print(value.unwrap_or(10)) 213 # 10 214 ``` 215 """ 216 if self._value is None: 217 return default 218 219 return self._value
Unwrap the inner value either returning if its not None
or returning default
.
Example
value = Some(5)
print(value.unwrap_or(10))
# 5
# Type hint is required here.
value: Option[int] = Some(None)
print(value.unwrap_or(10))
# 10
221 def unwrap_or_else(self, f: FnOnce[T], /) -> T: 222 """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value. 223 224 Example 225 ------- 226 ```py 227 value = Some(5) 228 print(value.unwrap_or_else(lambda: 10)) 229 # 5 230 231 value: Option[bool] = Some(None) 232 print(value.unwrap_or_else(lambda: True)) 233 # True 234 ``` 235 """ 236 if self._value is None: 237 return f() 238 239 return self._value
Unwrap the inner value either returning if its not None
or calling f
to get a default value.
Example
value = Some(5)
print(value.unwrap_or_else(lambda: 10))
# 5
value: Option[bool] = Some(None)
print(value.unwrap_or_else(lambda: True))
# True
241 @macros.unsafe 242 def unwrap_unchecked(self) -> T: 243 """Returns the contained Some value without checking that the value is not None. 244 245 Example 246 ------- 247 ```py 248 v: Option[float] = Some(1.2) 249 v.unwrap_unchecked() # 1.2 250 251 v: Option[float] = Some(None) 252 print(v.unwrap_unchecked()) # Undefined Behavior 253 ``` 254 """ 255 #! SAFETY: The caller guarantees that the value is not None. 256 return self._value # pyright: ignore
Returns the contained Some value without checking that the value is not None.
Example
v: Option[float] = Some(1.2)
v.unwrap_unchecked() # 1.2
v: Option[float] = Some(None)
print(v.unwrap_unchecked()) # Undefined Behavior
Safety ⚠️
Calling this method without knowing its output is considered undefined behavior.
258 def expect(self, message: str, /) -> T: 259 """Returns the contained `Some` value. 260 261 raises if the value is `None` with a custom provided `message`. 262 263 Example 264 ------- 265 ```py 266 value = Some("Hello") 267 268 print(value.expect("Value is None")) 269 # "Hello" 270 271 value: Option[str] = Some(None) 272 print(value.expect("Value is None")) 273 # RuntimeError("Value is None") 274 ``` 275 """ 276 if self._value is None: 277 raise RuntimeError(message) 278 279 return self._value
Returns the contained Some
value.
raises if the value is None
with a custom provided message
.
Example
value = Some("Hello")
print(value.expect("Value is None"))
# "Hello"
value: Option[str] = Some(None)
print(value.expect("Value is None"))
# RuntimeError("Value is None")
283 def map(self, f: Fn[T, U], /) -> Option[U]: 284 """Map the inner value to another type. Returning `Some(None)` if `T` is `None`. 285 286 Example 287 ------- 288 ```py 289 value = Some(5.0) 290 291 print(value.map(lambda x: x * 2.0)) 292 # Some(10.0) 293 294 value: Option[bool] = Some(None) 295 print(value) 296 # Some(None) 297 ``` 298 """ 299 if self._value is None: 300 return NOTHING 301 302 return Some(f(self._value))
Map the inner value to another type. Returning Some(None)
if T
is None
.
Example
value = Some(5.0)
print(value.map(lambda x: x * 2.0))
# Some(10.0)
value: Option[bool] = Some(None)
print(value)
# Some(None)
304 def map_or(self, default: U, f: Fn[T, U], /) -> U: 305 """Map the inner value to another type or return `default` if its `None`. 306 307 Example 308 ------- 309 ```py 310 value: Option[float] = Some(5.0) 311 312 # map to int. 313 print(value.map_or(0, int)) 314 # 6 315 316 value: Option[float] = Some(None) 317 print(value.map_or(0, int) 318 # 0 319 ``` 320 """ 321 if self._value is None: 322 return default 323 324 return f(self._value)
Map the inner value to another type or return default
if its None
.
Example
value: Option[float] = Some(5.0)
# map to int.
print(value.map_or(0, int))
# 6
value: Option[float] = Some(None)
print(value.map_or(0, int)
# 0
326 def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U: 327 """Map the inner value to another type, or return `default()` if its `None`. 328 329 Example 330 ------- 331 ```py 332 def default() -> int: 333 return sys.getsizeof(object()) 334 335 value: Option[float] = Some(5.0) 336 337 # map to int. 338 print(value.map_or_else(default, int)) 339 # 6 340 341 value: Option[float] = Some(None) 342 print(value.map_or_else(default, int) 343 # 28 <- size of object() 344 ``` 345 """ 346 if self._value is None: 347 return default() 348 349 return f(self._value)
Map the inner value to another type, or return default()
if its None
.
Example
def default() -> int:
return sys.getsizeof(object())
value: Option[float] = Some(5.0)
# map to int.
print(value.map_or_else(default, int))
# 6
value: Option[float] = Some(None)
print(value.map_or_else(default, int)
# 28 <- size of object()
351 def filter(self, predicate: Fn[T, bool]) -> Option[T]: 352 """Returns `Some(None)` if the contained value is `None`, 353 354 otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`. 355 356 Example 357 ------- 358 ```py 359 value = Some([1, 2, 3]) 360 361 print(value.filter(lambda x: 1 in x)) 362 # Some([1, 2, 3]) 363 364 value: Option[int] = Some([1, 2, 3]) # or Some(None) 365 print(value.filter(lambda x: 1 not in x)) 366 # None 367 ``` 368 """ 369 if (value := self._value) is not None: 370 if predicate(value): 371 return Some(value) 372 373 return NOTHING
Returns Some(None)
if the contained value is None
,
otherwise calls the predicate and returns Some(T)
if the predicate returns True
.
Example
value = Some([1, 2, 3])
print(value.filter(lambda x: 1 in x))
# Some([1, 2, 3])
value: Option[int] = Some([1, 2, 3]) # or Some(None)
print(value.filter(lambda x: 1 not in x))
# None
375 def ok_or(self, err: U) -> _result.Result[T, U]: 376 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. 377 378 Example 379 ------- 380 ```py 381 xyz: Option[str] = Some("foo") 382 assert xyz.ok_or(None) == Ok("foo") 383 384 xyz: Option[str] = Some(None) 385 assert xyz.ok_or(None) == Err(None) 386 ``` 387 """ 388 if self._value is None: 389 return _result.Err(err) 390 391 return _result.Ok(self._value)
Transforms the Option<T>
into a Result<T, E>
, mapping Some(v)
to Ok(v)
and None
to Err(err)
.
Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")
xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
393 def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]: 394 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. 395 396 Example 397 ------- 398 ```py 399 xyz: Option[str] = Some("foo") 400 assert xyz.ok_or(None) == Ok("foo") 401 402 xyz: Option[str] = Some(None) 403 assert xyz.ok_or(None) == Err(None) 404 ``` 405 """ 406 if self._value is None: 407 return _result.Err(err()) 408 409 return _result.Ok(self._value)
Transforms the Option<T>
into a Result<T, E>
, mapping Some(v)
to Ok(v)
and None
to Err(err())
.
Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")
xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
411 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 412 """Zips `self` with `other`. 413 414 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`. 415 416 Example 417 ------- 418 ```py 419 x = Some(1) 420 y = Some("hi") 421 z: Option[str] = Some(None) 422 423 assert x.zip(y) == Some((1, "hi")) 424 assert x.zip(z) == Some(None) 425 ``` 426 """ 427 if self._value is not None and other._value is not None: 428 return Some((self._value, other._value)) 429 430 return NOTHING
Zips self
with other
.
if self
is Some(s)
and other is Some(o)
, this returns Some((s, o))
otherwise None
.
Example
x = Some(1)
y = Some("hi")
z: Option[str] = Some(None)
assert x.zip(y) == Some((1, "hi"))
assert x.zip(z) == Some(None)
432 def zip_with( 433 self, other: Option[U], f: collections.Callable[[T, U], T_co] 434 ) -> Option[T_co]: 435 """Zips `self` with `other` using function `f`. 436 437 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`. 438 439 Example 440 ------- 441 ```py 442 @dataclass 443 class Point: 444 x: float 445 y: float 446 447 x, y = Some(32.1), Some(42.4) 448 assert x.zip_with(y, Point) == Some(Point(32.1, 42.4)) 449 ``` 450 """ 451 if self._value is not None and other._value is not None: 452 return Some(f(self._value, other._value)) 453 454 return NOTHING
Zips self
with other
using function f
.
if self
is Some(s)
and other is Some(o)
, this returns Some(f(s, o))
otherwise None
.
Example
@dataclass
class Point:
x: float
y: float
x, y = Some(32.1), Some(42.4)
assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
458 def take(self) -> Option[T]: 459 """Take the value from `self` Setting it to `None`, and then return `Some(v)`. 460 461 If you don't care about the original value, use `Option.clear()` instead. 462 463 Example 464 ------- 465 ```py 466 original = Some("Hi") 467 new = original.take() 468 469 print(original, new) 470 # None, Some("Hi") 471 ``` 472 """ 473 if self._value is None: 474 return NOTHING 475 476 val = self._value 477 self._value = None 478 return Some(val)
Take the value from self
Setting it to None
, and then return Some(v)
.
If you don't care about the original value, use Option.clear()
instead.
Example
original = Some("Hi")
new = original.take()
print(original, new)
# None, Some("Hi")
480 def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]: 481 """Take the value from `Self`, Setting it to `None` only if predicate returns `True`. 482 483 If you don't care about the original value, use `Option.clear_if()` instead. 484 485 Example 486 ------- 487 ```py 488 def validate(email: str) -> bool: 489 # you can obviously validate this better. 490 return email.find('@') == 1 491 492 original = Some("flex@gg.com") 493 valid = original.take_if(validate) 494 assert is_allowed.is_some() and original.is_none() 495 496 original = Some("mail.example.com") 497 invalid = original.take_if(validate) 498 assert invalid.is_none() and original.is_some() 499 ``` 500 """ 501 if self.map_or(False, predicate): 502 return self.take() 503 504 return NOTHING
Take the value from Self
, Setting it to None
only if predicate returns True
.
If you don't care about the original value, use Option.clear_if()
instead.
Example
def validate(email: str) -> bool:
# you can obviously validate this better.
return email.find('@') == 1
original = Some("flex@gg.com")
valid = original.take_if(validate)
assert is_allowed.is_some() and original.is_none()
original = Some("mail.example.com")
invalid = original.take_if(validate)
assert invalid.is_none() and original.is_some()
506 def clear(self) -> None: 507 """Clear the inner value, setting it to `None`. 508 509 If you care about the original value, use `Option.take()` instead. 510 511 Example 512 ------- 513 ```py 514 value = Some("Hello") 515 value.clear() 516 assert value.is_none() 517 ``` 518 """ 519 self._value = None
Clear the inner value, setting it to None
.
If you care about the original value, use Option.take()
instead.
Example
value = Some("Hello")
value.clear()
assert value.is_none()
521 def clear_if(self, predicate: Fn[T, bool]) -> None: 522 """Clear the inner value, setting it to `None` if the predicate returns `True`. 523 524 If you care about the original value, use `Option.take_if()` instead. 525 526 Example 527 ------- 528 ```py 529 value = Some("Hello") 530 value.clear_if(lambda x: x == "Hello") 531 assert value.is_none() 532 ``` 533 """ 534 if self._value is not None and predicate(self._value): 535 self._value = None
Clear the inner value, setting it to None
if the predicate returns True
.
If you care about the original value, use Option.take_if()
instead.
Example
value = Some("Hello")
value.clear_if(lambda x: x == "Hello")
assert value.is_none()
537 def replace(self, value: T) -> Option[T]: 538 """Replace the contained value with another value. 539 540 Use `Option.insert` if you want to return the original value 541 that got inserted instead of `self` 542 543 Example 544 ------- 545 ```py 546 value: Option[str] = Some(None) 547 value.replace("Hello") 548 # Some("Hello") 549 ``` 550 """ 551 self._value = value 552 return self
Replace the contained value with another value.
Use Option.insert
if you want to return the original value
that got inserted instead of self
Example
value: Option[str] = Some(None)
value.replace("Hello")
# Some("Hello")
554 def insert(self, value: T) -> T: 555 """Insert a value into the option, and then return a reference to it. 556 557 This will overwrite the old value if it was already contained. 558 559 Example 560 ------- 561 ```py 562 flag: Option[bool] = Some(None) 563 flag_ref = flag.insert(True) 564 assert flag_ref == True 565 assert flag.unwrap() == True 566 ``` 567 """ 568 self._value = value 569 return value
Insert a value into the option, and then return a reference to it.
This will overwrite the old value if it was already contained.
Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
571 def get_or_insert(self, value: T) -> T: 572 """Insert a value into the option if it was `None`, 573 and then return a reference to it. 574 575 Example 576 ------- 577 ```py 578 state: Option[bool] = Some(None) 579 assert state.get_or_insert(True) is True 580 assert state.get_or_insert(False) is True 581 ``` 582 """ 583 if self._value is not None: 584 return self._value 585 586 self._value = value 587 return value
Insert a value into the option if it was None
,
and then return a reference to it.
Example
state: Option[bool] = Some(None)
assert state.get_or_insert(True) is True
assert state.get_or_insert(False) is True
589 def get_or_insert_with(self, f: FnOnce[T]) -> T: 590 """Insert a value into the option computed from `f()` if it was `None`, 591 and then return a reference to it. 592 593 Example 594 ------- 595 ```py 596 flag: Option[bool] = Some(None) 597 flag_ref = flag.insert(True) 598 assert flag_ref == True 599 assert flag.unwrap() == True 600 ``` 601 """ 602 if self._value is not None: 603 return self._value 604 605 v = self._value = f() 606 return v
Insert a value into the option computed from f()
if it was None
,
and then return a reference to it.
Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
608 def and_ok(self, optb: Option[T]) -> Option[T]: 609 """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`. 610 611 aliases: `Option::and` 612 613 Example 614 ------- 615 ```py 616 x = Some(1) 617 y: Option[str] = Some(None) 618 assert x.and_ok(y) == Some(None) 619 620 x: Option[str] = Some(None) 621 y = Some(1) 622 assert x.and_ok(y) == Some(None) 623 624 x: Option[str] = Some("hi") 625 y = Some(100) 626 assert x.and_ok(y) == Some(100) 627 ``` 628 """ 629 if self._value is None or optb._value is None: 630 return optb 631 632 return NOTHING
Returns None
if self
or optb
is None
, otherwise return optb
.
aliases: Option::and
Example
x = Some(1)
y: Option[str] = Some(None)
assert x.and_ok(y) == Some(None)
x: Option[str] = Some(None)
y = Some(1)
assert x.and_ok(y) == Some(None)
x: Option[str] = Some("hi")
y = Some(100)
assert x.and_ok(y) == Some(100)
634 def and_then(self, f: Fn[T, Option[T]]) -> Option[T]: 635 """Returns `Some(None)` if the contained value is `None`, otherwise call `f()` 636 on `T` and return `Option[T]`. 637 638 Example 639 ------- 640 ```py 641 value = Some(5) 642 print(value.and_then(lambda x: Some(x * 2))) 643 # Some(10) 644 645 value: Option[int] = Some(None) 646 print(value.and_then(lambda x: Some(x * 2))) 647 # Some(None) 648 ``` 649 """ 650 if self._value is None: 651 return NOTHING 652 653 return f(self._value)
Returns Some(None)
if the contained value is None
, otherwise call f()
on T
and return Option[T]
.
Example
value = Some(5)
print(value.and_then(lambda x: Some(x * 2)))
# Some(10)
value: Option[int] = Some(None)
print(value.and_then(lambda x: Some(x * 2)))
# Some(None)
655 def inspect(self, f: Fn[T, typing.Any]) -> Option[T]: 656 """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing. 657 658 Example 659 ------- 660 ```py 661 def debug(x: str) -> None: 662 print("Debugging:", x) 663 664 value = Some("foo") 665 inner = value.inspect(debug).expect("no value to debug") 666 # prints: Debugging: "foo" 667 668 value: Option[str] = Some(None) 669 value.inspect(debug) # prints nothing 670 """ 671 if self._value is not None: 672 f(self._value) 673 674 return self
Calls f()
on the contained value if it was Some(v)
, otherwise does nothing.
Example
```py def debug(x: str) -> None: print("Debugging:", x)
value = Some("foo") inner = value.inspect(debug).expect("no value to debug")
prints: Debugging: "foo"
value: Option[str] = Some(None) value.inspect(debug) # prints nothing
678 def iter(self) -> _iter.ExactSizeIterator[T]: 679 """Returns an iterator over the contained value. 680 681 Example 682 ------- 683 ```py 684 from sain import Some 685 value = Some("gg").iter() 686 assert value.next() == Some("gg") 687 688 value: Option[int] = Some(None) 689 assert value.iter().next().is_none() 690 ``` 691 """ 692 if self._value is None: 693 return _iter.empty() 694 695 return _iter.once(self._value)
Returns an iterator over the contained value.
Example
from sain import Some
value = Some("gg")sain.iter()
assert value.next() == Some("gg")
value: Option[int] = Some(None)
assert value.iter().next().is_none()
699 def is_some(self) -> bool: 700 """Returns `True` if the contained value is not `None`, otherwise returns `False`. 701 702 Example 703 ------- 704 ```py 705 value = Some(5) 706 print(value.is_some()) 707 # True 708 709 value: Option[int] = Some(None) 710 print(value.is_some()) 711 # False 712 ``` 713 """ 714 return self._value is not None
Returns True
if the contained value is not None
, otherwise returns False
.
Example
value = Some(5)
print(value.is_some())
# True
value: Option[int] = Some(None)
print(value.is_some())
# False
716 def is_some_and(self, predicate: Fn[T, bool]) -> bool: 717 """Returns `True` if the contained value is not `None` and 718 the predicate returns `True`, otherwise returns `False`. 719 720 Example 721 ------- 722 ```py 723 value = Some(5) 724 print(value.is_some_and(lambda x: x > 3)) 725 # True 726 727 value: Option[int] = Some(None) 728 print(value.is_some_and(lambda x: x > 3)) 729 # False 730 ``` 731 """ 732 return self._value is not None and predicate(self._value)
Returns True
if the contained value is not None
and
the predicate returns True
, otherwise returns False
.
Example
value = Some(5)
print(value.is_some_and(lambda x: x > 3))
# True
value: Option[int] = Some(None)
print(value.is_some_and(lambda x: x > 3))
# False
734 def is_none(self) -> bool: 735 """Returns `True` if the contained value is `None`, otherwise returns `False`. 736 737 Example 738 ------- 739 ```py 740 value = Some(5) 741 print(value.is_none()) 742 # False 743 744 value: Option[int] = Some(None) 745 print(value.is_none()) 746 # True 747 ``` 748 """ 749 return self._value is None
Returns True
if the contained value is None
, otherwise returns False
.
Example
value = Some(5)
print(value.is_none())
# False
value: Option[int] = Some(None)
print(value.is_none())
# True
751 def is_none_or(self, f: Fn[T, bool]) -> bool: 752 """Returns `True` if the contained value is `None` or the predicate returns `True`, 753 otherwise returns `False`. 754 755 Example 756 ------- 757 ```py 758 value = Some(5) 759 print(value.is_none_or(lambda x: x > 3)) 760 # False 761 762 value: Option[int] = Some(None) 763 print(value.is_none_or(lambda x: x > 3)) 764 # True 765 ``` 766 """ 767 match self._value: 768 case None: 769 return True 770 case x: 771 return f(x)
Returns True
if the contained value is None
or the predicate returns True
,
otherwise returns False
.
Example
value = Some(5)
print(value.is_none_or(lambda x: x > 3))
# False
value: Option[int] = Some(None)
print(value.is_none_or(lambda x: x > 3))
# True
1002@rustc_diagnostic_item("Iter") 1003@typing.final 1004@diagnostic 1005class Iter(typing.Generic[Item], Iterator[Item]): 1006 """a lazy iterator that has its items ready in-memory. 1007 1008 This is similar to Rust `std::slice::Iter<T>` item which iterables can build 1009 from this via `.iter()` method. 1010 1011 Example 1012 ------- 1013 ```py 1014 iterator = Iter([1, 2, 3]) 1015 1016 # Limit the results to 2. 1017 for item in iterator.take(2): 1018 print(item) 1019 # 1 1020 # 2 1021 1022 # Filter the results. 1023 for item in iterator.filter(lambda item: item > 1): 1024 print(item) 1025 # 2 1026 # 3 1027 # 3 1028 1029 # Indexing is supported. 1030 print(iterator[0]) 1031 # 1 1032 ``` 1033 1034 Parameters 1035 ---------- 1036 items: `Iterable[Item]` 1037 The items to iterate over. This can be anything that implements `__iter__` and `__next__`. 1038 """ 1039 1040 __slots__ = ("_it",) 1041 1042 def __init__(self, iterable: collections.Iterable[Item]) -> None: 1043 self._it = iter(iterable) 1044 1045 def clone(self) -> Iter[Item]: 1046 """Return a copy of this iterator. 1047 1048 ```py 1049 it = Iter([1, 2, 3]) 1050 1051 for i in it.clone(): 1052 ... 1053 1054 # The actual iterator hasn't been exhausted. 1055 assert it.count() == 3 1056 ``` 1057 """ 1058 return Iter(copy.copy(self._it)) 1059 1060 def __next__(self) -> Item: 1061 return next(self._it) 1062 1063 def __getitem__(self, index: int) -> Item: 1064 return self.skip(index).first().unwrap_or_else(oob) 1065 1066 def __contains__(self, item: Item) -> bool: 1067 return item in self._it
a lazy iterator that has its items ready in-memory.
This is similar to Rust std::slice::Iter<T>
item which iterables can build
from this via .iter()
method.
Example
iterator = Iter([1, 2, 3])
# Limit the results to 2.
for item in iterator.take(2):
print(item)
# 1
# 2
# Filter the results.
for item in iterator.filter(lambda item: item > 1):
print(item)
# 2
# 3
# 3
# Indexing is supported.
print(iterator[0])
# 1
Parameters
- items (
Iterable[Item]
): The items to iterate over. This can be anything that implements__iter__
and__next__
.
Implementations
This class implements Iter in Rust.
1045 def clone(self) -> Iter[Item]: 1046 """Return a copy of this iterator. 1047 1048 ```py 1049 it = Iter([1, 2, 3]) 1050 1051 for i in it.clone(): 1052 ... 1053 1054 # The actual iterator hasn't been exhausted. 1055 assert it.count() == 3 1056 ``` 1057 """ 1058 return Iter(copy.copy(self._it))
Return a copy of this iterator.
it = Iter([1, 2, 3])
for i in it.clone():
...
# The actual iterator hasn't been exhausted.
assert it.count() == 3
126@rustc_diagnostic_item("Iterator") 127class Iterator( 128 typing.Generic[Item], 129 abc.ABC, 130 _default.Default["Empty[Item]"], 131): 132 """An abstract interface for dealing with iterators. 133 134 This is exactly the same trait as `core::iter::Iterator` trait from Rust. 135 136 This is the main interface that any type can implement by basically inheriting from it. 137 The method `__next__` is the only method that needs to be implemented, You get all the other methods for free. 138 139 If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementers 140 and type hints. 141 142 Example 143 ------- 144 ```py 145 @dataclass 146 class Counter(Iterator[int]): 147 start: int = 0 148 stop: int | None = None 149 150 # implement the required method. 151 def __next__(self) -> int: 152 result = self.start 153 self.start += 1 154 155 if self.stop is not None and result >= self.stop: 156 raise StopIteration 157 158 return result 159 160 counter = Counter(start=0, stop=10) 161 for i in counter.map(lambda x: x * 2): # multiply each number 162 ... 163 ``` 164 """ 165 166 __slots__ = () 167 168 @abc.abstractmethod 169 def __next__(self) -> Item: 170 raise NotImplementedError 171 172 ################### 173 # const functions # 174 ################### 175 176 @staticmethod 177 @typing.final 178 def default() -> Empty[Item]: 179 """Return the default iterator for this type. It returns an empty iterator. 180 181 Example 182 ------- 183 ```py 184 it: Iterator[int] = Iter.default() 185 assert t.next().is_none() 186 ``` 187 """ 188 return Empty() 189 190 @typing.overload 191 def collect(self) -> collections.MutableSequence[Item]: ... 192 193 @typing.overload 194 def collect( 195 self, *, cast: collections.Callable[[Item], OtherItem] 196 ) -> collections.MutableSequence[OtherItem]: ... 197 198 @typing.final 199 def collect( 200 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 201 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 202 """Collects all items in the iterator into a sequence. 203 204 Example 205 ------- 206 ```py 207 iterator = Iter(range(3)) 208 iterator.collect() 209 # [0, 1, 2, 3] 210 iterator.collect(cast=str) # Map each element and collect it. 211 # ['0', '1', '2', '3'] 212 ``` 213 214 Parameters 215 ---------- 216 cast: `T | None` 217 An optional type to cast the items into. 218 If not provided the items will be returned as it's original type. 219 """ 220 if cast is not None: 221 return [cast(i) for i in self] 222 223 return [_ for _ in self] 224 225 @typing.final 226 def collect_into(self, collection: Collector[Item]) -> None: 227 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 228 229 Example 230 ------- 231 ```py 232 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 233 uniques = set() 234 iterator.collect_into(uniques) 235 # assert uniques == {1, 2, 3, 4, 6} 236 ``` 237 238 Parameters 239 ---------- 240 collection: `MutableSequence[T]` | `set[T]` 241 The collection to extend the items in this iterator with. 242 """ 243 if isinstance(collection, collections.MutableSequence): 244 collection.extend(_ for _ in self) 245 elif isinstance(collection, collections.MutableSet): 246 collection.update(_ for _ in self) 247 else: 248 for idx, item in enumerate(self): 249 collection[idx] = item 250 251 @typing.final 252 def to_vec(self) -> Vec[Item]: 253 """Convert this iterator into `Vec[T]`. 254 255 Example 256 ------- 257 ```py 258 it = sain.iter.once(0) 259 vc = it.to_vec() 260 261 assert to_vec == [0] 262 ``` 263 """ 264 from sain.collections.vec import Vec 265 266 return Vec(_ for _ in self) 267 268 @typing.final 269 def sink(self) -> None: 270 """Consume all elements from this iterator, flushing it into the sink. 271 272 Example 273 ------- 274 ```py 275 it = Iter((1, 2, 3)) 276 it.sink() 277 assert it.next().is_none() 278 ``` 279 """ 280 for _ in self: 281 pass 282 283 @typing.final 284 def raw_parts(self) -> collections.Generator[Item, None, None]: 285 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 286 287 ```py 288 it = Iter("cba") 289 sort = sorted(it.raw_parts()) 290 291 assert it.count() == 0 292 assert sort == ["a", "b", "c"] 293 ``` 294 """ 295 for item in self: 296 yield item 297 298 ################## 299 # default impl's # 300 ################## 301 302 def next(self) -> Option[Item]: 303 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 304 305 Example 306 ------- 307 ```py 308 iterator = Iter(["1", "2"]) 309 assert iterator.next() == Some("1") 310 assert iterator.next() == Some("2") 311 assert iterator.next().is_none() 312 ``` 313 """ 314 try: 315 return _option.Some(self.__next__()) 316 except StopIteration: 317 # ! SAFETY: No more items in the iterator. 318 return _option.NOTHING 319 320 def cloned(self) -> Cloned[Item]: 321 """Creates an iterator which shallow copies its elements by reference. 322 323 .. note:: 324 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 325 on each item that is being yielded. 326 327 Example 328 ------- 329 ```py 330 @dataclass 331 class User: 332 users_ids: list[int] = [] 333 334 # An iterator which elements points to the same user. 335 user = User() 336 it = Iter((user, user)) 337 338 for u in it.cloned(): 339 u.user_ids.append(1) 340 341 # We iterated over the same user pointer twice and appended "1" 342 # since `copy` returns a shallow copy of nested structures. 343 assert len(user.user_ids) == 2 344 ``` 345 """ 346 return Cloned(self) 347 348 def copied(self) -> Copied[Item]: 349 """Creates an iterator which copies all of its elements by value. 350 351 If you only need a copy of the item reference, Use `.cloned()` instead. 352 353 .. note:: 354 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 355 on each item that is being yielded. 356 357 Example 358 ------- 359 ```py 360 @dataclass 361 class User: 362 users_ids: list[int] = [] 363 364 # An iterator which elements points to the same user. 365 user = User() 366 it = Iter((user, user)) 367 368 for u in it.copied(): 369 # A new list is created for each item. 370 u.user_ids.append(1) 371 372 # The actual list is untouched since we consumed a deep copy of it. 373 assert len(user.user_ids) == 0 374 ``` 375 """ 376 return Copied(self) 377 378 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 379 """Maps each item in the iterator to another type. 380 381 Example 382 ------- 383 ```py 384 iterator = Iter(["1", "2", "3"]).map(int) 385 386 for item in iterator: 387 assert isinstance(item, int) 388 ``` 389 390 Parameters 391 ---------- 392 predicate: `Callable[[Item], OtherItem]` 393 The function to map each item in the iterator to the other type. 394 """ 395 return Map(self, fn) 396 397 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 398 """Filters the iterator to only yield items that match the predicate. 399 400 Example 401 ------- 402 ```py 403 places = Iter(['London', 'Paris', 'Los Angeles']) 404 for place in places.filter(lambda place: place.startswith('L')): 405 print(place) 406 407 # London 408 # Los Angeles 409 ``` 410 """ 411 return Filter(self, predicate) 412 413 def take(self, count: int) -> Take[Item]: 414 """Take the first number of items until the number of items 415 are yielded or the end of the iterator is exhausted. 416 417 Example 418 ------- 419 ```py 420 iterator = Iter(['c', 'x', 'y']) 421 422 for x in iterator.take(2): 423 assert x in ('c', 'x') 424 425 # <Iter(['c', 'x'])> 426 ``` 427 """ 428 return Take(self, count) 429 430 def skip(self, count: int) -> Skip[Item]: 431 """Skips the first number of items in the iterator. 432 433 Example 434 ------- 435 ```py 436 iterator = Iter((1, 2, 3, 4)) 437 for i in iterator.skip(2): 438 print(i) 439 440 # 3 441 # 4 442 ``` 443 """ 444 return Skip(self, count) 445 446 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 447 """Create a new iterator that yields a tuple of the index and item. 448 449 Example 450 ------- 451 ```py 452 iterator = Iter([1, 2, 3]) 453 for index, item in iterator.enumerate(): 454 print(index, item) 455 456 # 0 1 457 # 1 2 458 # 2 3 459 ``` 460 """ 461 return Enumerate(self, start) 462 463 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 464 """yields items from the iterator while predicate returns `True`. 465 466 The rest of the items are discarded as soon as the predicate returns `False` 467 468 Example 469 ------- 470 ```py 471 iterator = Iter(['a', 'ab', 'xd', 'ba']) 472 for x in iterator.take_while(lambda x: 'a' in x): 473 print(x) 474 475 # a 476 # ab 477 ``` 478 479 Parameters 480 ---------- 481 predicate: `collections.Callable[[Item], bool]` 482 The function to predicate each item in the iterator. 483 """ 484 return TakeWhile(self, f) 485 486 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 487 """Yields items from the iterator while predicate returns `False`. 488 489 Example 490 ------- 491 ```py 492 iterator = Iter(['a', 'ab', 'xd', 'ba']) 493 for x in iterator.drop_while(lambda x: 'a' in x): 494 print(x) 495 496 # xd 497 # ba 498 ``` 499 500 Parameters 501 ---------- 502 predicate: `collections.Callable[[Item], bool]` 503 The function to predicate each item in the iterator. 504 """ 505 return DropWhile(self, f) 506 507 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 508 """Returns an iterator over `chunk_size` elements of the iterator at a time, 509 starting at the beginning of the iterator. 510 511 Example 512 ------- 513 ```py 514 iter = Iter(['a', 'b', 'c', 'd', 'e']) 515 chunks = iter.chunks() 516 assert chunks.next().unwrap() == ['a', 'b'] 517 assert chunks.next().unwrap() == ['c', 'd'] 518 assert chunks.next().unwrap() == ['e'] 519 assert chunks.next().is_none() 520 ``` 521 """ 522 return Chunks(self, chunk_size) 523 524 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 525 """Return `True` if all items in the iterator match the predicate. 526 527 Example 528 ------- 529 ```py 530 iterator = Iter([1, 2, 3]) 531 if iterator.all(lambda item: isinstance(item, int)): 532 print("yes") 533 ``` 534 535 Parameters 536 ---------- 537 predicate: `collections.Callable[[Item], bool]` 538 The function to test each item in the iterator. 539 """ 540 return all(predicate(item) for item in self) 541 542 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 543 """`True` if any items in the iterator match the predicate. 544 545 Example 546 ------- 547 ```py 548 iterator = Iter([1, 2, 3]) 549 if iterator.any(lambda item: isinstance(item, int)): 550 print("At least one item is an int.") 551 # At least one item is an int. 552 ``` 553 554 Parameters 555 ---------- 556 predicate: `collections.Callable[[Item], bool]` 557 The function to test each item in the iterator. 558 """ 559 return any(predicate(item) for item in self) 560 561 def zip( 562 self, other: collections.Iterable[OtherItem] 563 ) -> Iter[tuple[Item, OtherItem]]: 564 """Zips the iterator with another iterable. 565 566 Example 567 ------- 568 ```py 569 xs = Iter([1, 2, 3]) 570 ys = [4, 5, 6] 571 572 iter = xs.zip(ys) 573 574 assert iter.next().unwrap() == (1, 4) 575 assert iter.next().unwrap() == (2, 5) 576 assert iter.next().unwrap() == (3, 6) 577 ``` 578 579 Parameters 580 ---------- 581 other: `Iterable[OtherItem]` 582 The iterable to zip with. 583 """ 584 return Iter(zip(self.raw_parts(), other)) 585 586 def sort( 587 self, 588 *, 589 key: collections.Callable[[Item], SupportsRichComparison], 590 reverse: bool = False, 591 ) -> collections.MutableSequence[Item]: 592 """Sorts the iterator elements and return it in a mutable sequence. 593 594 Example 595 ------- 596 ```py 597 iterator = Iter([3, 1, 6, 7]) 598 for item in iterator.sort(key=lambda item: item < 3): 599 print(item) 600 # 1 601 # 3 602 # 6 603 # 7 604 ``` 605 606 Parameters 607 ---------- 608 key: `collections.Callable[[Item], Any]` 609 The function to sort by. 610 reverse: `bool` 611 Whether to reverse the sort. 612 """ 613 return sorted([_ for _ in self], key=key, reverse=reverse) 614 615 def reversed(self) -> Iter[Item]: 616 """Returns a new iterator that yields the items in the iterator in reverse order. 617 618 This consumes this iterator into a sequence and return a new iterator containing all of the elements 619 in reversed order. 620 621 Example 622 ------- 623 ```py 624 iterator = Iter([3, 1, 6, 7]) 625 for item in iterator.reversed(): 626 print(item) 627 # 7 628 # 6 629 # 1 630 # 3 631 ``` 632 """ 633 # NOTE: In order to reverse the iterator we need to 634 # first collect it into some collection. 635 # FIXME: specialize an impl for this. 636 return Iter(reversed([_ for _ in self])) 637 638 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 639 """Returns a new iterator that yields all items from both iterators. 640 641 Example 642 ------- 643 ```py 644 iterator = Iter([1, 2, 3]) 645 other = [4, 5, 6] 646 647 for item in iterator.union(other): 648 print(item) 649 # 1 650 # 2 651 # 3 652 # 4 653 # 5 654 # 6 655 ``` 656 657 Parameters 658 ---------- 659 other: `Iter[Item]` 660 The iterable to union with. 661 """ 662 return Iter(itertools.chain(self.raw_parts(), other)) 663 664 def first(self) -> Option[Item]: 665 """Returns the first item in the iterator. 666 667 Example 668 ------- 669 ```py 670 iterator = Iter([3, 1, 6, 7]) 671 iterator.first().is_some_and(lambda x: x == 3) 672 ``` 673 """ 674 return self.take(1).next() 675 676 def last(self) -> Option[Item]: 677 """Returns the last item in the iterator. 678 679 Example 680 ------- 681 ```py 682 iterator = Iter([3, 1, 6, 7]) 683 iterator.last().is_some_and(lambda x: x == 7) 684 ``` 685 """ 686 return self.reversed().first() 687 688 def count(self) -> int: 689 """Consume this iterator, returning the count of elements it has. 690 691 Example 692 ------- 693 ```py 694 it = Iter(range(3)) 695 assert it.count() == 3 696 ``` 697 """ 698 count = 0 699 for _ in self: 700 count += 1 701 702 return count 703 704 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 705 """Searches for an element of an iterator that satisfies a predicate. 706 707 If you want the position of the element, use `Iterator.position` instead. 708 709 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 710 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 711 712 Example 713 ------- 714 ```py 715 it = Iter(range(10)) 716 item = it.find(lambda num: num > 5) 717 print(item) # 6 718 ``` 719 """ 720 for item in self: 721 if predicate(item): 722 return _option.Some(item) 723 724 # no more items 725 return _option.NOTHING 726 727 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 728 """Searches for the position of an element in the iterator that satisfies a predicate. 729 730 If you want the object itself, use `Iterator.find` instead. 731 732 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 733 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 734 735 Example 736 ------- 737 ```py 738 it = Iter(range(10)) 739 position = it.find(lambda num: num > 5) 740 assert position.unwrap() == 6 741 ``` 742 """ 743 for position, value in self.enumerate(): 744 if predicate(value): 745 return _option.Some(position) 746 747 # no more items 748 return _option.NOTHING 749 750 def fold( 751 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 752 ) -> OtherItem: 753 """Folds every element into an accumulator by applying an operation, returning the final result. 754 755 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 756 The closure returns the value that the accumulator should have for the next iteration. 757 758 The initial value is the value the accumulator will have on the first call. 759 760 After applying this closure to every element of the iterator, fold() returns the accumulator. 761 762 This operation is sometimes called ‘reduce’ or ‘inject’. 763 764 Example 765 ------- 766 ```py 767 a = Iter([1, 2, 3, 4]) 768 sum = a.fold(0, lambda acc, elem: acc + elem) 769 assert sum == 10 770 ``` 771 """ 772 accum = init 773 while True: 774 try: 775 x = self.__next__() 776 accum = f(accum, x) 777 except StopIteration: 778 break 779 780 return accum 781 782 def advance_by(self, n: int) -> _result.Result[None, int]: 783 """Advances the iterator by `n` elements. 784 785 Returns `Result[None, int]`, where `Ok(None)` means the iterator 786 advanced successfully, and `Err(int)` if `None` encountered, where `int` 787 represents the remaining number of steps that could not be advanced because the iterator ran out. 788 789 Example 790 ------- 791 ```py 792 it = into_iter([1, 2, 3, 4]) 793 assert it.advance_by(2).is_ok() 794 assert it.next() == Some(3) 795 assert it.advance_by(0).is_ok() 796 assert it.advance_by(100) == Err(99) 797 ``` 798 """ 799 for i in range(n): 800 try: 801 self.__next__() 802 except StopIteration: 803 return _result.Err(n - i) 804 805 return _result.Ok(None) 806 807 def nth(self, n: int) -> Option[Item]: 808 """Returns the `n`th element of the iterator 809 810 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 811 value. 812 813 Note all elements before `n` will be skipped / consumed. 814 815 Example 816 ------- 817 ```py 818 a = into_iter([1, 2, 3]) 819 assert a.iter().nth(1) == Some(2) 820 ``` 821 """ 822 for _ in range(n): 823 try: 824 self.__next__() 825 except StopIteration: 826 return _option.NOTHING 827 828 return self.next() 829 830 def sum(self: Sum) -> int: 831 """Sums an iterator of a possible type `T` that can be converted to an integer. 832 833 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 834 835 Example 836 ------- 837 ```py 838 numbers: Iterator[str] = Iter(["1", "2", "3"]) 839 total = numbers.sum() 840 assert total == 6 841 ``` 842 """ 843 return sum(int(_) for _ in self) 844 845 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 846 """Calls `func` on each item in the iterator. 847 848 Example 849 ------- 850 ```py 851 iterator = Iter([1, 2, 3]) 852 iterator.for_each(lambda item: print(item)) 853 # 1 854 # 2 855 # 3 856 ``` 857 858 Parameters 859 ---------- 860 func: `collections.Callable[[Item], typing.Any]` 861 The function to call on each item in the iterator. 862 """ 863 for item in self: 864 func(item) 865 866 async def async_for_each( 867 self, 868 func: collections.Callable[ 869 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 870 ], 871 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 872 """Calls the async function on each item in the iterator *concurrently*. 873 874 Concurrently meaning that the next item will not wait for other items 875 to finish to execute, each item gets called in a separate task. 876 877 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 878 which will need to be handled by the caller. 879 880 Example 881 ------- 882 ```py 883 async def create_user(username: str) -> None: 884 await aiohttp.request("POST", f'.../{username}') 885 886 async def main(): 887 users = sain.into_iter(["Danny", "Flower"]) 888 match await users.async_for_each(lambda username: create_user(username)): 889 case Ok(result): 890 # all good 891 case Err(why): 892 print(f"couldn't gather all futures, err={why}") 893 ``` 894 895 Parameters 896 ---------- 897 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 898 The async function to call on each item in the iterator. 899 """ 900 return await futures.join(*(func(item) for item in self)) 901 902 def __reversed__(self) -> Iter[Item]: 903 return self.reversed() 904 905 def __repr__(self) -> str: 906 return "<Iterator>" 907 908 def __copy__(self) -> Cloned[Item]: 909 return self.cloned() 910 911 def __deepcopy__( 912 self, memo: collections.MutableMapping[int, typing.Any], / 913 ) -> Copied[Item]: 914 return self.copied() 915 916 def __len__(self) -> int: 917 return self.count() 918 919 def __iter__(self) -> Iterator[Item]: 920 return self
An abstract interface for dealing with iterators.
This is exactly the same trait as core::iter::Iterator
trait from Rust.
This is the main interface that any type can implement by basically inheriting from it.
The method __next__
is the only method that needs to be implemented, You get all the other methods for free.
If you want to use a ready iterator for general purposes, Use Iter
. This interface is only for implementers
and type hints.
Example
@dataclass
class Counter(Iterator[int]):
start: int = 0
stop: int | None = None
# implement the required method.
def __next__(self) -> int:
result = self.start
self.start += 1
if self.stop is not None and result >= self.stop:
raise StopIteration
return result
counter = Counter(start=0, stop=10)
for i in counter.map(lambda x: x * 2): # multiply each number
...
Implementations
This class implements Iterator in Rust.
176 @staticmethod 177 @typing.final 178 def default() -> Empty[Item]: 179 """Return the default iterator for this type. It returns an empty iterator. 180 181 Example 182 ------- 183 ```py 184 it: Iterator[int] = Iter.default() 185 assert t.next().is_none() 186 ``` 187 """ 188 return Empty()
Return the default iterator for this type. It returns an empty iterator.
Example
it: Iterator[int] = Iter.default()
assert t.next().is_none()
198 @typing.final 199 def collect( 200 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 201 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 202 """Collects all items in the iterator into a sequence. 203 204 Example 205 ------- 206 ```py 207 iterator = Iter(range(3)) 208 iterator.collect() 209 # [0, 1, 2, 3] 210 iterator.collect(cast=str) # Map each element and collect it. 211 # ['0', '1', '2', '3'] 212 ``` 213 214 Parameters 215 ---------- 216 cast: `T | None` 217 An optional type to cast the items into. 218 If not provided the items will be returned as it's original type. 219 """ 220 if cast is not None: 221 return [cast(i) for i in self] 222 223 return [_ for _ in self]
Collects all items in the iterator into a sequence.
Example
iterator = Iter(range(3))
iterator.collect()
# [0, 1, 2, 3]
iterator.collect(cast=str) # Map each element and collect it.
# ['0', '1', '2', '3']
Parameters
- cast (
T | None
): An optional type to cast the items into. If not provided the items will be returned as it's original type.
225 @typing.final 226 def collect_into(self, collection: Collector[Item]) -> None: 227 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 228 229 Example 230 ------- 231 ```py 232 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 233 uniques = set() 234 iterator.collect_into(uniques) 235 # assert uniques == {1, 2, 3, 4, 6} 236 ``` 237 238 Parameters 239 ---------- 240 collection: `MutableSequence[T]` | `set[T]` 241 The collection to extend the items in this iterator with. 242 """ 243 if isinstance(collection, collections.MutableSequence): 244 collection.extend(_ for _ in self) 245 elif isinstance(collection, collections.MutableSet): 246 collection.update(_ for _ in self) 247 else: 248 for idx, item in enumerate(self): 249 collection[idx] = item
Consume this iterator, extending all items in the iterator into a mutable collection
.
Example
iterator = Iter([1, 1, 2, 3, 4, 2, 6])
uniques = set()
iterator.collect_into(uniques)
# assert uniques == {1, 2, 3, 4, 6}
Parameters
- collection (
MutableSequence[T]
|set[T]
): The collection to extend the items in this iterator with.
251 @typing.final 252 def to_vec(self) -> Vec[Item]: 253 """Convert this iterator into `Vec[T]`. 254 255 Example 256 ------- 257 ```py 258 it = sain.iter.once(0) 259 vc = it.to_vec() 260 261 assert to_vec == [0] 262 ``` 263 """ 264 from sain.collections.vec import Vec 265 266 return Vec(_ for _ in self)
Convert this iterator into Vec[T]
.
Example
it = sain.iter.once(0)
vc = it.to_vec()
assert to_vec == [0]
268 @typing.final 269 def sink(self) -> None: 270 """Consume all elements from this iterator, flushing it into the sink. 271 272 Example 273 ------- 274 ```py 275 it = Iter((1, 2, 3)) 276 it.sink() 277 assert it.next().is_none() 278 ``` 279 """ 280 for _ in self: 281 pass
Consume all elements from this iterator, flushing it into the sink.
Example
it = Iter((1, 2, 3))
it.sink()
assert it.next().is_none()
283 @typing.final 284 def raw_parts(self) -> collections.Generator[Item, None, None]: 285 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 286 287 ```py 288 it = Iter("cba") 289 sort = sorted(it.raw_parts()) 290 291 assert it.count() == 0 292 assert sort == ["a", "b", "c"] 293 ``` 294 """ 295 for item in self: 296 yield item
Decompose this iterator into a Generator[Item]
that yields all of the remaining items.
it = Iter("cba")
sort = sorted(it.raw_parts())
assert it.count() == 0
assert sort == ["a", "b", "c"]
302 def next(self) -> Option[Item]: 303 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 304 305 Example 306 ------- 307 ```py 308 iterator = Iter(["1", "2"]) 309 assert iterator.next() == Some("1") 310 assert iterator.next() == Some("2") 311 assert iterator.next().is_none() 312 ``` 313 """ 314 try: 315 return _option.Some(self.__next__()) 316 except StopIteration: 317 # ! SAFETY: No more items in the iterator. 318 return _option.NOTHING
Advance the iterator, Returning the next item, Some(None)
if all items yielded.
Example
iterator = Iter(["1", "2"])
assert iterator.next() == Some("1")
assert iterator.next() == Some("2")
assert iterator.next().is_none()
320 def cloned(self) -> Cloned[Item]: 321 """Creates an iterator which shallow copies its elements by reference. 322 323 .. note:: 324 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 325 on each item that is being yielded. 326 327 Example 328 ------- 329 ```py 330 @dataclass 331 class User: 332 users_ids: list[int] = [] 333 334 # An iterator which elements points to the same user. 335 user = User() 336 it = Iter((user, user)) 337 338 for u in it.cloned(): 339 u.user_ids.append(1) 340 341 # We iterated over the same user pointer twice and appended "1" 342 # since `copy` returns a shallow copy of nested structures. 343 assert len(user.user_ids) == 2 344 ``` 345 """ 346 return Cloned(self)
Creates an iterator which shallow copies its elements by reference.
This method calls copy.copy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.cloned():
u.user_ids.append(1)
# We iterated over the same user pointer twice and appended "1"
# since `copy` returns a shallow copy of nested structures.
assert len(user.user_ids) == 2
348 def copied(self) -> Copied[Item]: 349 """Creates an iterator which copies all of its elements by value. 350 351 If you only need a copy of the item reference, Use `.cloned()` instead. 352 353 .. note:: 354 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 355 on each item that is being yielded. 356 357 Example 358 ------- 359 ```py 360 @dataclass 361 class User: 362 users_ids: list[int] = [] 363 364 # An iterator which elements points to the same user. 365 user = User() 366 it = Iter((user, user)) 367 368 for u in it.copied(): 369 # A new list is created for each item. 370 u.user_ids.append(1) 371 372 # The actual list is untouched since we consumed a deep copy of it. 373 assert len(user.user_ids) == 0 374 ``` 375 """ 376 return Copied(self)
Creates an iterator which copies all of its elements by value.
If you only need a copy of the item reference, Use .cloned()
instead.
This method simply calls copy.deepcopy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.copied():
# A new list is created for each item.
u.user_ids.append(1)
# The actual list is untouched since we consumed a deep copy of it.
assert len(user.user_ids) == 0
378 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 379 """Maps each item in the iterator to another type. 380 381 Example 382 ------- 383 ```py 384 iterator = Iter(["1", "2", "3"]).map(int) 385 386 for item in iterator: 387 assert isinstance(item, int) 388 ``` 389 390 Parameters 391 ---------- 392 predicate: `Callable[[Item], OtherItem]` 393 The function to map each item in the iterator to the other type. 394 """ 395 return Map(self, fn)
Maps each item in the iterator to another type.
Example
iterator = Iter(["1", "2", "3"]).map(int)
for item in iterator:
assert isinstance(item, int)
Parameters
- predicate (
Callable[[Item], OtherItem]
): The function to map each item in the iterator to the other type.
397 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 398 """Filters the iterator to only yield items that match the predicate. 399 400 Example 401 ------- 402 ```py 403 places = Iter(['London', 'Paris', 'Los Angeles']) 404 for place in places.filter(lambda place: place.startswith('L')): 405 print(place) 406 407 # London 408 # Los Angeles 409 ``` 410 """ 411 return Filter(self, predicate)
Filters the iterator to only yield items that match the predicate.
Example
places = Iter(['London', 'Paris', 'Los Angeles'])
for place in places.filter(lambda place: place.startswith('L')):
print(place)
# London
# Los Angeles
413 def take(self, count: int) -> Take[Item]: 414 """Take the first number of items until the number of items 415 are yielded or the end of the iterator is exhausted. 416 417 Example 418 ------- 419 ```py 420 iterator = Iter(['c', 'x', 'y']) 421 422 for x in iterator.take(2): 423 assert x in ('c', 'x') 424 425 # <Iter(['c', 'x'])> 426 ``` 427 """ 428 return Take(self, count)
Take the first number of items until the number of items are yielded or the end of the iterator is exhausted.
Example
iterator = Iter(['c', 'x', 'y'])
for x in iterator.take(2):
assert x in ('c', 'x')
# <Iter(['c', 'x'])>
430 def skip(self, count: int) -> Skip[Item]: 431 """Skips the first number of items in the iterator. 432 433 Example 434 ------- 435 ```py 436 iterator = Iter((1, 2, 3, 4)) 437 for i in iterator.skip(2): 438 print(i) 439 440 # 3 441 # 4 442 ``` 443 """ 444 return Skip(self, count)
Skips the first number of items in the iterator.
Example
iterator = Iter((1, 2, 3, 4))
for i in iterator.skip(2):
print(i)
# 3
# 4
446 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 447 """Create a new iterator that yields a tuple of the index and item. 448 449 Example 450 ------- 451 ```py 452 iterator = Iter([1, 2, 3]) 453 for index, item in iterator.enumerate(): 454 print(index, item) 455 456 # 0 1 457 # 1 2 458 # 2 3 459 ``` 460 """ 461 return Enumerate(self, start)
Create a new iterator that yields a tuple of the index and item.
Example
iterator = Iter([1, 2, 3])
for index, item in iterator.enumerate():
print(index, item)
# 0 1
# 1 2
# 2 3
463 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 464 """yields items from the iterator while predicate returns `True`. 465 466 The rest of the items are discarded as soon as the predicate returns `False` 467 468 Example 469 ------- 470 ```py 471 iterator = Iter(['a', 'ab', 'xd', 'ba']) 472 for x in iterator.take_while(lambda x: 'a' in x): 473 print(x) 474 475 # a 476 # ab 477 ``` 478 479 Parameters 480 ---------- 481 predicate: `collections.Callable[[Item], bool]` 482 The function to predicate each item in the iterator. 483 """ 484 return TakeWhile(self, f)
yields items from the iterator while predicate returns True
.
The rest of the items are discarded as soon as the predicate returns False
Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.take_while(lambda x: 'a' in x):
print(x)
# a
# ab
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to predicate each item in the iterator.
486 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 487 """Yields items from the iterator while predicate returns `False`. 488 489 Example 490 ------- 491 ```py 492 iterator = Iter(['a', 'ab', 'xd', 'ba']) 493 for x in iterator.drop_while(lambda x: 'a' in x): 494 print(x) 495 496 # xd 497 # ba 498 ``` 499 500 Parameters 501 ---------- 502 predicate: `collections.Callable[[Item], bool]` 503 The function to predicate each item in the iterator. 504 """ 505 return DropWhile(self, f)
Yields items from the iterator while predicate returns False
.
Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.drop_while(lambda x: 'a' in x):
print(x)
# xd
# ba
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to predicate each item in the iterator.
507 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 508 """Returns an iterator over `chunk_size` elements of the iterator at a time, 509 starting at the beginning of the iterator. 510 511 Example 512 ------- 513 ```py 514 iter = Iter(['a', 'b', 'c', 'd', 'e']) 515 chunks = iter.chunks() 516 assert chunks.next().unwrap() == ['a', 'b'] 517 assert chunks.next().unwrap() == ['c', 'd'] 518 assert chunks.next().unwrap() == ['e'] 519 assert chunks.next().is_none() 520 ``` 521 """ 522 return Chunks(self, chunk_size)
Returns an iterator over chunk_size
elements of the iterator at a time,
starting at the beginning of the iterator.
Example
iter = Iter(['a', 'b', 'c', 'd', 'e'])
chunks = iter.chunks()
assert chunks.next().unwrap() == ['a', 'b']
assert chunks.next().unwrap() == ['c', 'd']
assert chunks.next().unwrap() == ['e']
assert chunks.next().is_none()
524 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 525 """Return `True` if all items in the iterator match the predicate. 526 527 Example 528 ------- 529 ```py 530 iterator = Iter([1, 2, 3]) 531 if iterator.all(lambda item: isinstance(item, int)): 532 print("yes") 533 ``` 534 535 Parameters 536 ---------- 537 predicate: `collections.Callable[[Item], bool]` 538 The function to test each item in the iterator. 539 """ 540 return all(predicate(item) for item in self)
Return True
if all items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
if iterator.all(lambda item: isinstance(item, int)):
print("yes")
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to test each item in the iterator.
542 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 543 """`True` if any items in the iterator match the predicate. 544 545 Example 546 ------- 547 ```py 548 iterator = Iter([1, 2, 3]) 549 if iterator.any(lambda item: isinstance(item, int)): 550 print("At least one item is an int.") 551 # At least one item is an int. 552 ``` 553 554 Parameters 555 ---------- 556 predicate: `collections.Callable[[Item], bool]` 557 The function to test each item in the iterator. 558 """ 559 return any(predicate(item) for item in self)
True
if any items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
print("At least one item is an int.")
# At least one item is an int.
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to test each item in the iterator.
561 def zip( 562 self, other: collections.Iterable[OtherItem] 563 ) -> Iter[tuple[Item, OtherItem]]: 564 """Zips the iterator with another iterable. 565 566 Example 567 ------- 568 ```py 569 xs = Iter([1, 2, 3]) 570 ys = [4, 5, 6] 571 572 iter = xs.zip(ys) 573 574 assert iter.next().unwrap() == (1, 4) 575 assert iter.next().unwrap() == (2, 5) 576 assert iter.next().unwrap() == (3, 6) 577 ``` 578 579 Parameters 580 ---------- 581 other: `Iterable[OtherItem]` 582 The iterable to zip with. 583 """ 584 return Iter(zip(self.raw_parts(), other))
Zips the iterator with another iterable.
Example
xs = Iter([1, 2, 3])
ys = [4, 5, 6]
iter = xs.zip(ys)
assert iter.next().unwrap() == (1, 4)
assert iter.next().unwrap() == (2, 5)
assert iter.next().unwrap() == (3, 6)
Parameters
- other (
Iterable[OtherItem]
): The iterable to zip with.
586 def sort( 587 self, 588 *, 589 key: collections.Callable[[Item], SupportsRichComparison], 590 reverse: bool = False, 591 ) -> collections.MutableSequence[Item]: 592 """Sorts the iterator elements and return it in a mutable sequence. 593 594 Example 595 ------- 596 ```py 597 iterator = Iter([3, 1, 6, 7]) 598 for item in iterator.sort(key=lambda item: item < 3): 599 print(item) 600 # 1 601 # 3 602 # 6 603 # 7 604 ``` 605 606 Parameters 607 ---------- 608 key: `collections.Callable[[Item], Any]` 609 The function to sort by. 610 reverse: `bool` 611 Whether to reverse the sort. 612 """ 613 return sorted([_ for _ in self], key=key, reverse=reverse)
Sorts the iterator elements and return it in a mutable sequence.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.sort(key=lambda item: item < 3):
print(item)
# 1
# 3
# 6
# 7
Parameters
- key (
collections.Callable[[Item], Any]
): The function to sort by. - reverse (
bool
): Whether to reverse the sort.
615 def reversed(self) -> Iter[Item]: 616 """Returns a new iterator that yields the items in the iterator in reverse order. 617 618 This consumes this iterator into a sequence and return a new iterator containing all of the elements 619 in reversed order. 620 621 Example 622 ------- 623 ```py 624 iterator = Iter([3, 1, 6, 7]) 625 for item in iterator.reversed(): 626 print(item) 627 # 7 628 # 6 629 # 1 630 # 3 631 ``` 632 """ 633 # NOTE: In order to reverse the iterator we need to 634 # first collect it into some collection. 635 # FIXME: specialize an impl for this. 636 return Iter(reversed([_ for _ in self]))
Returns a new iterator that yields the items in the iterator in reverse order.
This consumes this iterator into a sequence and return a new iterator containing all of the elements in reversed order.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.reversed():
print(item)
# 7
# 6
# 1
# 3
638 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 639 """Returns a new iterator that yields all items from both iterators. 640 641 Example 642 ------- 643 ```py 644 iterator = Iter([1, 2, 3]) 645 other = [4, 5, 6] 646 647 for item in iterator.union(other): 648 print(item) 649 # 1 650 # 2 651 # 3 652 # 4 653 # 5 654 # 6 655 ``` 656 657 Parameters 658 ---------- 659 other: `Iter[Item]` 660 The iterable to union with. 661 """ 662 return Iter(itertools.chain(self.raw_parts(), other))
Returns a new iterator that yields all items from both iterators.
Example
iterator = Iter([1, 2, 3])
other = [4, 5, 6]
for item in iterator.union(other):
print(item)
# 1
# 2
# 3
# 4
# 5
# 6
Parameters
- other (
Iter[Item]
): The iterable to union with.
664 def first(self) -> Option[Item]: 665 """Returns the first item in the iterator. 666 667 Example 668 ------- 669 ```py 670 iterator = Iter([3, 1, 6, 7]) 671 iterator.first().is_some_and(lambda x: x == 3) 672 ``` 673 """ 674 return self.take(1).next()
Returns the first item in the iterator.
Example
iterator = Iter([3, 1, 6, 7])
iterator.first().is_some_and(lambda x: x == 3)
676 def last(self) -> Option[Item]: 677 """Returns the last item in the iterator. 678 679 Example 680 ------- 681 ```py 682 iterator = Iter([3, 1, 6, 7]) 683 iterator.last().is_some_and(lambda x: x == 7) 684 ``` 685 """ 686 return self.reversed().first()
Returns the last item in the iterator.
Example
iterator = Iter([3, 1, 6, 7])
iterator.last().is_some_and(lambda x: x == 7)
688 def count(self) -> int: 689 """Consume this iterator, returning the count of elements it has. 690 691 Example 692 ------- 693 ```py 694 it = Iter(range(3)) 695 assert it.count() == 3 696 ``` 697 """ 698 count = 0 699 for _ in self: 700 count += 1 701 702 return count
Consume this iterator, returning the count of elements it has.
Example
it = Iter(range(3))
assert it.count() == 3
704 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 705 """Searches for an element of an iterator that satisfies a predicate. 706 707 If you want the position of the element, use `Iterator.position` instead. 708 709 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 710 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 711 712 Example 713 ------- 714 ```py 715 it = Iter(range(10)) 716 item = it.find(lambda num: num > 5) 717 print(item) # 6 718 ``` 719 """ 720 for item in self: 721 if predicate(item): 722 return _option.Some(item) 723 724 # no more items 725 return _option.NOTHING
Searches for an element of an iterator that satisfies a predicate.
If you want the position of the element, use Iterator.position
instead.
find()
takes a lambda that returns true or false. It applies this closure to each element of the iterator,
and if any of them return true, then find() returns Some(element)
. If they all return false, it returns None.
Example
it = Iter(range(10))
item = it.find(lambda num: num > 5)
print(item) # 6
727 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 728 """Searches for the position of an element in the iterator that satisfies a predicate. 729 730 If you want the object itself, use `Iterator.find` instead. 731 732 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 733 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 734 735 Example 736 ------- 737 ```py 738 it = Iter(range(10)) 739 position = it.find(lambda num: num > 5) 740 assert position.unwrap() == 6 741 ``` 742 """ 743 for position, value in self.enumerate(): 744 if predicate(value): 745 return _option.Some(position) 746 747 # no more items 748 return _option.NOTHING
Searches for the position of an element in the iterator that satisfies a predicate.
If you want the object itself, use Iterator.find
instead.
position()
takes a lambda that returns true or false. It applies this closure to each element of the iterator,
and if any of them return true, then position() returns Some(position_of_element)
. If they all return false, it returns None.
Example
it = Iter(range(10))
position = it.find(lambda num: num > 5)
assert position.unwrap() == 6
750 def fold( 751 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 752 ) -> OtherItem: 753 """Folds every element into an accumulator by applying an operation, returning the final result. 754 755 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 756 The closure returns the value that the accumulator should have for the next iteration. 757 758 The initial value is the value the accumulator will have on the first call. 759 760 After applying this closure to every element of the iterator, fold() returns the accumulator. 761 762 This operation is sometimes called ‘reduce’ or ‘inject’. 763 764 Example 765 ------- 766 ```py 767 a = Iter([1, 2, 3, 4]) 768 sum = a.fold(0, lambda acc, elem: acc + elem) 769 assert sum == 10 770 ``` 771 """ 772 accum = init 773 while True: 774 try: 775 x = self.__next__() 776 accum = f(accum, x) 777 except StopIteration: 778 break 779 780 return accum
Folds every element into an accumulator by applying an operation, returning the final result.
fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. The closure returns the value that the accumulator should have for the next iteration.
The initial value is the value the accumulator will have on the first call.
After applying this closure to every element of the iterator, fold() returns the accumulator.
This operation is sometimes called ‘reduce’ or ‘inject’.
Example
a = Iter([1, 2, 3, 4])
sum = a.fold(0, lambda acc, elem: acc + elem)
assert sum == 10
782 def advance_by(self, n: int) -> _result.Result[None, int]: 783 """Advances the iterator by `n` elements. 784 785 Returns `Result[None, int]`, where `Ok(None)` means the iterator 786 advanced successfully, and `Err(int)` if `None` encountered, where `int` 787 represents the remaining number of steps that could not be advanced because the iterator ran out. 788 789 Example 790 ------- 791 ```py 792 it = into_iter([1, 2, 3, 4]) 793 assert it.advance_by(2).is_ok() 794 assert it.next() == Some(3) 795 assert it.advance_by(0).is_ok() 796 assert it.advance_by(100) == Err(99) 797 ``` 798 """ 799 for i in range(n): 800 try: 801 self.__next__() 802 except StopIteration: 803 return _result.Err(n - i) 804 805 return _result.Ok(None)
Advances the iterator by n
elements.
Returns Result[None, int]
, where Ok(None)
means the iterator
advanced successfully, and Err(int)
if None
encountered, where int
represents the remaining number of steps that could not be advanced because the iterator ran out.
Example
it = into_iter([1, 2, 3, 4])
assert it.advance_by(2).is_ok()
assert it.next() == Some(3)
assert it.advance_by(0).is_ok()
assert it.advance_by(100) == Err(99)
807 def nth(self, n: int) -> Option[Item]: 808 """Returns the `n`th element of the iterator 809 810 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 811 value. 812 813 Note all elements before `n` will be skipped / consumed. 814 815 Example 816 ------- 817 ```py 818 a = into_iter([1, 2, 3]) 819 assert a.iter().nth(1) == Some(2) 820 ``` 821 """ 822 for _ in range(n): 823 try: 824 self.__next__() 825 except StopIteration: 826 return _option.NOTHING 827 828 return self.next()
Returns the n
th 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)
830 def sum(self: Sum) -> int: 831 """Sums an iterator of a possible type `T` that can be converted to an integer. 832 833 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 834 835 Example 836 ------- 837 ```py 838 numbers: Iterator[str] = Iter(["1", "2", "3"]) 839 total = numbers.sum() 840 assert total == 6 841 ``` 842 """ 843 return sum(int(_) for _ in self)
Sums an iterator of a possible type T
that can be converted to an integer.
where T
is a typeof (int
, float
, str
, ReadableBuffer
, SupportsTrunc
, SupportsIndex
).
Example
numbers: Iterator[str] = Iter(["1", "2", "3"])
total = numbers.sum()
assert total == 6
845 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 846 """Calls `func` on each item in the iterator. 847 848 Example 849 ------- 850 ```py 851 iterator = Iter([1, 2, 3]) 852 iterator.for_each(lambda item: print(item)) 853 # 1 854 # 2 855 # 3 856 ``` 857 858 Parameters 859 ---------- 860 func: `collections.Callable[[Item], typing.Any]` 861 The function to call on each item in the iterator. 862 """ 863 for item in self: 864 func(item)
Calls func
on each item in the iterator.
Example
iterator = Iter([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
- func (
collections.Callable[[Item], typing.Any]
): The function to call on each item in the iterator.
866 async def async_for_each( 867 self, 868 func: collections.Callable[ 869 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 870 ], 871 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 872 """Calls the async function on each item in the iterator *concurrently*. 873 874 Concurrently meaning that the next item will not wait for other items 875 to finish to execute, each item gets called in a separate task. 876 877 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 878 which will need to be handled by the caller. 879 880 Example 881 ------- 882 ```py 883 async def create_user(username: str) -> None: 884 await aiohttp.request("POST", f'.../{username}') 885 886 async def main(): 887 users = sain.into_iter(["Danny", "Flower"]) 888 match await users.async_for_each(lambda username: create_user(username)): 889 case Ok(result): 890 # all good 891 case Err(why): 892 print(f"couldn't gather all futures, err={why}") 893 ``` 894 895 Parameters 896 ---------- 897 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 898 The async function to call on each item in the iterator. 899 """ 900 return await futures.join(*(func(item) for item in self))
Calls the async function on each item in the iterator concurrently.
Concurrently meaning that the next item will not wait for other items to finish to execute, each item gets called in a separate task.
After all the tasks finish, a Result[list[T], JoinError]
will be returned,
which will need to be handled by the caller.
Example
async def create_user(username: str) -> None:
await aiohttp.request("POST", f'.../{username}')
async def main():
users = sain.into_iter(["Danny", "Flower"])
match await users.async_for_each(lambda username: create_user(username)):
case Ok(result):
# all good
case Err(why):
print(f"couldn't gather all futures, err={why}")
Parameters
- func (
collections.Callable[[Item], Coroutine[None, Any, Any]]
): The async function to call on each item in the iterator.
733@rustc_diagnostic_item("todo") 734def todo(message: LiteralString | None = None) -> typing.NoReturn: 735 """A place holder that indicates unfinished code. 736 737 Example 738 ------- 739 ```py 740 from sain import todo 741 742 def from_json(payload: dict[str, int]) -> int: 743 # Calling this function will raise `Error`. 744 todo() 745 ``` 746 747 Parameters 748 ---------- 749 message : `str | None` 750 Multiple optional arguments to pass if the error was raised. 751 """ 752 raise RuntimeWarning( 753 f"not yet implemented: {message}" if message else "not yet implemented" 754 )
A place holder that indicates unfinished code.
Example
from sain import todo
def from_json(payload: dict[str, int]) -> int:
# Calling this function will raise `Error`.
todo()
Parameters
- message (
str | None
): Multiple optional arguments to pass if the error was raised.
Implementations
This function implements todo in Rust.
633@rustc_diagnostic_item("deprecated") 634def deprecated( 635 *, 636 obj: collections.Callable[P, U] | None = None, 637 since: typing.Literal["CURRENT_VERSION"] | LiteralString | None = None, 638 removed_in: LiteralString | None = None, 639 use_instead: LiteralString | None = None, 640 hint: LiteralString | None = None, 641) -> ( 642 collections.Callable[P, U] 643 | collections.Callable[ 644 [collections.Callable[P, U]], 645 collections.Callable[P, U], 646 ] 647): 648 """A decorator that marks a function as deprecated. 649 650 An attempt to call the object that's marked will cause a runtime warn. 651 652 Example 653 ------- 654 ```py 655 from sain import deprecated 656 657 @deprecated( 658 since = "1.0.0", 659 removed_in ="3.0.0", 660 use_instead = "UserImpl()", 661 hint = "Hint for ux." 662 ) 663 class User: 664 # calling the decorator is not necessary. 665 @deprecated 666 def username(self) -> str: 667 ... 668 669 user = User() # This will cause a warning at runtime. 670 671 ``` 672 673 Parameters 674 ---------- 675 since : `str` 676 The version that the function was deprecated. the `CURRENT_VERSION` is used internally only. 677 removed_in : `str | None` 678 If provided, It will log when will the object will be removed in. 679 use_instead : `str | None` 680 If provided, This should be the alternative object name that should be used instead. 681 hint: `str` 682 An optional hint for the user. 683 """ 684 685 def _create_message( 686 f: typing.Any, 687 ) -> str: 688 msg = f"{_obj_type(f)} `{f.__module__}.{f.__name__}` is deprecated." 689 690 if since is not None: 691 if since == "CURRENT_VERSION": 692 from ._misc import __version__ 693 694 msg += " since " + __version__ 695 else: 696 msg += " since " + since 697 698 if removed_in: 699 msg += f" Scheduled for removal in `{removed_in}`." 700 701 if use_instead is not None: 702 msg += f" Use `{use_instead}` instead." 703 704 if hint: 705 msg += f" Hint: {hint}" 706 return msg 707 708 def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]: 709 message = _create_message(func) 710 711 @functools.wraps(func) 712 def wrapper(*args: P.args, **kwargs: P.kwargs) -> U: 713 _warn("\033[93m" + message + "\033[0m", warn_ty=DeprecationWarning) 714 return func(*args, **kwargs) 715 716 # idk why pyright doesn't know the type of wrapper. 717 m = inspect.cleandoc(f"\n# Warning ⚠️\n{message}.") 718 if wrapper.__doc__: 719 # append this message to an existing document. 720 wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + m 721 else: 722 wrapper.__doc__ = m 723 724 return wrapper 725 726 # marked only. 727 if obj is not None: 728 return decorator(obj) 729 730 return decorator
A decorator that marks a function as deprecated.
An attempt to call the object that's marked will cause a runtime warn.
Example
from sain import deprecated
@deprecated(
since = "1.0.0",
removed_in ="3.0.0",
use_instead = "UserImpl()",
hint = "Hint for ux."
)
class User:
# calling the decorator is not necessary.
@deprecated
def username(self) -> str:
...
user = User() # This will cause a warning at runtime.
Parameters
- since (
str
): The version that the function was deprecated. theCURRENT_VERSION
is used internally only. - removed_in (
str | None
): If provided, It will log when will the object will be removed in. - use_instead (
str | None
): If provided, This should be the alternative object name that should be used instead. - hint (
str
): An optional hint for the user.
Implementations
This function implements deprecated in Rust.
776@rustc_diagnostic_item("unimplemented") 777def unimplemented( 778 *, 779 obj: collections.Callable[P, U] | None = None, 780 message: LiteralString | None = None, 781 available_in: LiteralString | None = None, 782) -> ( 783 collections.Callable[P, U] 784 | collections.Callable[ 785 [collections.Callable[P, U]], 786 collections.Callable[P, U], 787 ] 788): 789 """A decorator that marks an object as unimplemented. 790 791 An attempt to call the object that's marked will cause a runtime warn. 792 793 Example 794 ------- 795 ```py 796 from sain import unimplemented 797 798 @unimplemented # Can be used without calling 799 class User: 800 ... 801 802 @unimplemented(message="Not ready", available_in="2.0.0") # Or with parameters 803 class Config: 804 ... 805 ``` 806 807 Parameters 808 ---------- 809 message : `str | None` 810 An optional message to be displayed when the function is called. Otherwise default message will be used. 811 available_in : `str | None` 812 If provided, This will be shown as what release this object be implemented. 813 """ 814 815 def _create_message(f: typing.Any) -> str: 816 msg = ( 817 message 818 or f"{_obj_type(f)} `{f.__module__}.{f.__name__}` is not yet implemented." 819 ) 820 821 if available_in: 822 msg += f" Available in `{available_in}`." 823 return msg 824 825 def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]: 826 msg = _create_message(func) 827 828 @functools.wraps(func) 829 def wrapper(*args: P.args, **kwargs: P.kwargs) -> U: 830 _warn("\033[93m" + msg + "\033[0m", warn_ty=RuntimeWarning) 831 return func(*args, **kwargs) 832 833 m = f"\n# Warning ⚠️\n{msg}." 834 if wrapper.__doc__: 835 # Append the new documentation string to the existing docstring. 836 wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + m 837 else: 838 # Assign the new documentation string as the docstring when no existing docstring is present. 839 wrapper.__doc__ = m 840 return wrapper 841 842 if obj is not None: 843 return decorator(obj) 844 845 return decorator
A decorator that marks an object as unimplemented.
An attempt to call the object that's marked will cause a runtime warn.
Example
from sain import unimplemented
@unimplemented # Can be used without calling
class User:
...
@unimplemented(message="Not ready", available_in="2.0.0") # Or with parameters
class Config:
...
Parameters
- message (
str | None
): An optional message to be displayed when the function is called. Otherwise default message will be used. - available_in (
str | None
): If provided, This will be shown as what release this object be implemented.
Implementations
This function implements unimplemented in Rust.
848@rustc_diagnostic_item("doc") 849def doc( 850 path: Read, 851) -> collections.Callable[ 852 [collections.Callable[P, U]], 853 collections.Callable[P, U], 854]: 855 """Set `path` to be the object's documentation. 856 857 Example 858 ------- 859 ```py 860 from sain import doc 861 from pathlib import Path 862 863 @doc(Path("../README.md")) 864 class builtins: 865 @doc("bool.html") 866 def bool_docs() -> None: 867 ... 868 ``` 869 870 Parameters 871 ---------- 872 path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]` 873 The path to read the content from. 874 """ 875 876 def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]: 877 with open(path, "r") as file: 878 f.__doc__ = file.read() 879 880 return lambda *args, **kwargs: f(*args, **kwargs) 881 882 return decorator
Set path
to be the object's documentation.
Example
from sain import doc
from pathlib import Path
@doc(Path("../README.md"))
class builtins:
@doc("bool.html")
def bool_docs() -> None:
...
Parameters
- path (
type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]
): The path to read the content from.
Implementations
This function implements doc in Rust.
503@rustc_diagnostic_item("include_str") 504def include_str(file: LiteralString) -> LiteralString: 505 """Includes a file as literal `str`. 506 507 This function is not magic, It is literally defined as 508 509 ```py 510 with open(file, "r") as f: 511 return f.read() 512 ``` 513 514 The file name can may be either a relative to the current file or a complete path. 515 516 Example 517 ------- 518 ```py 519 from sain.macros import include_str 520 521 def entry() -> None: 522 ... 523 524 entry.__doc__ = include_str("README.md") 525 526 ``` 527 """ 528 with open(file, "r") as buf: 529 return buf.read() # pyright: ignore - simulates a `&'static str` slice.
Includes a file as literal str
.
This function is not magic, It is literally defined as
with open(file, "r") as f:
return f.read()
The file name can may be either a relative to the current file or a complete path.
Example
from sain.macros import include_str
def entry() -> None:
...
entry.__doc__ = include_str("README.md")
Implementations
This function implements include_str in Rust.
473@rustc_diagnostic_item("include_bytes") 474def include_bytes(file: LiteralString) -> bytes: 475 """Includes a file as `bytes`. 476 477 This function is not magic, It is literally defined as 478 479 ```py 480 with open(file, "rb") as f: 481 return f.read() 482 ``` 483 484 The file name can may be either a relative to the current file or a complete path. 485 486 Example 487 ------- 488 File "spanish.in": 489 ```text 490 adiós 491 ``` 492 File "main.py": 493 ```py 494 from sain.macros import include_bytes 495 buffer = include_bytes("spanish.in") 496 assert buffer.decode() == "adiós" 497 ``` 498 """ 499 with open(file, "rb") as buf: 500 return buf.read()
Includes a file as bytes
.
This function is not magic, It is literally defined as
with open(file, "rb") as f:
return f.read()
The file name can may be either a relative to the current file or a complete path.
Example
File "spanish.in":
adiós
File "main.py":
from sain.macros import include_bytes
buffer = include_bytes("spanish.in")
assert buffer.decode() == "adiós"
Implementations
This function implements include_bytes in Rust.
433@rustc_diagnostic_item("assert_eq") 434def assert_eq(left: T, right: T) -> None: 435 """Asserts that two expressions are equal to each other. 436 437 This exactly as `assert left == right`, but includes a useful message in case of failure. 438 439 Example 440 ------- 441 ```py 442 from sain.macros import assert_eq 443 a = 3 444 b = 1 + 2 445 assert_eq(a, b) 446 ``` 447 """ 448 assert left == right, ( 449 f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"' 450 )
Asserts that two expressions are equal to each other.
This exactly as assert left == right
, but includes a useful message in case of failure.
Example
from sain.macros import assert_eq
a = 3
b = 1 + 2
assert_eq(a, b)
Implementations
This function implements assert_eq in Rust.
453@rustc_diagnostic_item("assert_ne") 454def assert_ne(left: T, right: T) -> None: 455 """Asserts that two expressions are not equal to each other. 456 457 This exactly as `assert left == right`, but includes a useful message in case of failure. 458 459 Example 460 ------- 461 ```py 462 from sain.macros import assert_ne 463 a = 3 464 b = 2 + 2 465 assert_ne(a, b) 466 ``` 467 """ 468 assert left != right, ( 469 f'assertion `left != right` failed\nleft: "{left!r}"\nright: "{right!r}"' 470 )
Asserts that two expressions are not equal to each other.
This exactly as assert left == right
, but includes a useful message in case of failure.
Example
from sain.macros import assert_ne
a = 3
b = 2 + 2
assert_ne(a, b)
Implementations
This function implements assert_ne in Rust.
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
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
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
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
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
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.")
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
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
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
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}")
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
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
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
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
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))
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)
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 Resultdefault
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
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)
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)
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.
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
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
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
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
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
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
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
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)
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
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
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
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
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
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
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
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 Resultdefault
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
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)
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)
70@rustc_diagnostic_item("Vec") 71@typing.final 72class Vec(typing.Generic[T], collections.MutableSequence[T], SpecContains[T]): 73 """A contiguous growable alternative to the builtin `list` with extra functionalities. 74 75 The layout of `Vec<T>` is exactly the same as `list<T>`. Which means `list<T>`'s methods are inherited into `Vec<T>`. 76 77 Example 78 ------- 79 ```py 80 names = Vec() 81 names.push('foo') 82 names.push('bar') 83 84 print(names) # ['foo', 'bar'] 85 assert names.len() == 2 86 ``` 87 88 Constructing 89 ------------ 90 * `Vec()`: Create an empty vec, this will not allocate the initial buffer. 91 * `Vec(other_list)`: Create a vec which points to `other_list` 92 * `Vec([1, 2, 3])`: Create a vec with `[1, 2, 3]` pre-allocated. 93 * `Vec(iterable)`: Create a vec from an `iterable`, This is `O(n)` where `n` is the number of elements, 94 since it copies `iterable`'s elements into a new list. 95 * `Vec.with_capacity(5)`: Create a vec that can hold up to 5 elements 96 97 Iterating over `Vec` 98 ------------------- 99 There're two ways to iterate over a `Vec`. The first is to normally use `for` loop. 100 101 ```py 102 for i in names: 103 print(i) 104 105 # foo 106 # bar 107 ``` 108 109 The second is to use `Vec.iter`, which yields all items in this `Vec` from start to end. 110 Then the iterator gets exhausted as usual, See `sain.Iterator`. 111 112 ```py 113 iterator = names.iter() 114 for name in iterator.map(str.upper): 115 print(name) 116 117 # FOO 118 # BAR 119 120 # No more items, The actual vec is left unchanged. 121 assert iterator.next().is_none() 122 ``` 123 124 ## Comparison operators 125 A `Vec` may be compared with another `Vec` or a `list`, any other type will return `False` 126 127 ```py 128 vec = Vec([1, 2, 3]) 129 assert vec == [1, 2, 3] # True 130 assert vec != (1, 2, 3) 131 ``` 132 133 Zero-Copy 134 --------- 135 A vec that gets initialized from a `list` will *point* to it and doesn't copy it. 136 So any element that gets appended to the list will also get pushed into the vec 137 that's pointing to it and vice-versa. 138 139 ```py 140 cells: list[str] = [] 141 vec = Vec(cells) # This DOES NOT copy the `cells`. 142 143 cells.append("foo") 144 vec[0] == "foo" # True 145 ``` 146 147 If you want an owned `Vec` that doesn't point to the original list, 148 copy the list into a new `Vec`. 149 150 ```py 151 from sain import Vec 152 153 # Copy `cells` into a vec. 154 vec = Vec(cells[:]) 155 cells.append("bar") 156 157 vec[1] # IndexError: "bar" doesn't exist in vec. 158 ``` 159 """ 160 161 __slots__ = ("_ptr", "_capacity") 162 163 @typing.overload 164 def __init__(self) -> None: ... 165 166 @typing.overload 167 def __init__(self, iterable: collections.Iterable[T]) -> None: ... 168 169 def __init__(self, iterable: collections.Iterable[T] | None = None) -> None: 170 """Create an empty `Vec<T>`. 171 172 The vector will not allocate until elements are pushed onto it. 173 174 Example 175 ------ 176 ```py 177 vec: Vec[float] = Vec() 178 ``` 179 """ 180 # We won't allocate to build the list here. 181 # Instead, On first push. 182 if isinstance(iterable, list): 183 # Calling `list()` on another list will copy it, So instead we just point to it. 184 self._ptr = iterable 185 elif isinstance(iterable, Vec): 186 self._ptr = iterable._ptr 187 # any other iterable that ain't a list needs to get copied into a new list. 188 else: 189 self._ptr: list[T] | None = list(iterable) if iterable else None 190 191 self._capacity: int | None = None 192 193 @classmethod 194 def with_capacity(cls, capacity: int) -> Vec[T]: 195 """Create a new `Vec` with at least the specified capacity. 196 This vec will be able to hold `capacity` elements without pushing further. 197 198 The capacity may dynamically grow if `Vec.reserve` is called at runtime. 199 200 Check out `Vec.push_within_capacity` as well. 201 202 Example 203 ------- 204 ```py 205 vec = Vec.with_capacity(3) 206 assert vec.len() == 0 and vec.capacity() >= 3 207 208 vec.push(1) 209 vec.push(2) 210 vec.push(3) 211 print(vec.len()) # 3 212 213 # This won't push. 214 vec.push(4) 215 ``` 216 """ 217 v = cls() 218 v._capacity = capacity 219 return v 220 221 def as_ref(self) -> Slice[T]: 222 """Return an immutable view over this vector elements. 223 224 No copying occurs. 225 226 Example 227 ------- 228 ```py 229 def send_bytes(v: Slice[int]) -> None: 230 # access `vec` in a read-only mode. 231 socket.write_all(v) 232 233 buf = Vec([1, 2, 3]) 234 send_bytes(buf.as_ref()) 235 ``` 236 """ 237 return Slice(self) 238 239 def as_mut(self) -> SliceMut[T]: 240 """Return a mutable view over this vector elements. 241 242 No copying occurs. 243 244 Example 245 ------- 246 ```py 247 def read_bytes(buf: SliceMut[int]) -> None: 248 # similar to how `Write::write_to_end` requires a `&mut [u8]` 249 socket.read_to_end(buf) 250 251 buf: Vec[int] = Vec() 252 read_bytes(buf.as_mut()) # or just `buf` 253 assert vec.len() >= 1 254 ``` 255 """ 256 return SliceMut(self) 257 258 def len(self) -> int: 259 """Return the number of elements in this vector. 260 261 Example 262 ------- 263 ```py 264 vec = Vec((1,2,3)) 265 266 assert vec.len() == 3 267 ``` 268 """ 269 return self.__len__() 270 271 def capacity(self) -> int: 272 """Return the capacity of this vector if set, 0 if not . 273 274 The number `0` here has two different Meanings: 275 - The first means the `Vec` doesn't have a specific capacity set. 276 - The second means the `Vec` literally initialized with `0` capacity. 277 278 Example 279 ------- 280 ```py 281 vec_with_cap = Vec.with_capacity(3) 282 assert vec_with_cap.capacity().unwrap() == 3 283 284 vec = Vec([1, 2, 3]) 285 assert vec.capacity() == 0 286 ``` 287 """ 288 return 0 if self._capacity is None else self._capacity 289 290 def iter(self) -> _iter.TrustedIter[T]: 291 """Returns an iterator over this vector elements. 292 293 Example 294 ------- 295 ```py 296 vec = Vec([1 ,2, 3]) 297 for element in vec.iter(): 298 print(element) 299 ``` 300 """ 301 return _iter.TrustedIter(self._ptr or ()) 302 303 def is_empty(self) -> bool: 304 """Returns true if the vector contains no elements. 305 306 Inlined into: 307 ```py 308 not self.vec 309 ``` 310 """ 311 return not self._ptr 312 313 def leak(self) -> list[T]: 314 """Consumes and leaks the Vec, returning a mutable reference to the contents. 315 316 After calling this, this vec will no longer reference the underlying buffer, 317 therefore, it becomes unusable. 318 319 if `self` is uninitialized, an empty list is returned. 320 321 This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without 322 any copies. 323 324 Example 325 ------- 326 ```py 327 x = Vec([1, 2, 3]) 328 owned = x.leak() 329 # x is now unusable. 330 owned[0] += 1 331 assert owned == [2, 2, 3] 332 ``` 333 """ 334 if self._ptr is not None: 335 # don't point to `_ptr` anymore. 336 tmp = self._ptr 337 del self._ptr 338 return tmp 339 340 return [] 341 342 def split_off(self, at: int) -> Vec[T]: 343 """Split the vector off at the specified position, returning a new 344 vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`. 345 346 if this vec is empty, `self` is returned unchanged. 347 348 Example 349 ------- 350 ```py 351 origin = Vec((1, 2, 3, 4)) 352 split = vec.split_off(2) 353 354 print(origin, split) # [1, 2], [3, 4] 355 ``` 356 357 Raises 358 ------ 359 `IndexError` 360 This method will raise if `at` > `len(self)` 361 """ 362 len_ = self.len() 363 if at > len_: 364 raise IndexError( 365 f"Index `at` ({at}) should be <= than len of vector ({len_}) " 366 ) from None 367 368 # Either the list is empty or uninit. 369 if not self._ptr: 370 return self 371 372 split = self[at:len_] # split the items into a new vec. 373 del self._ptr[at:len_] # remove the items from the original list. 374 return split 375 376 def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 377 """Split the first and rest elements of the vector, If empty, `None` is returned. 378 379 Example 380 ------- 381 ```py 382 vec = Vec([1, 2, 3]) 383 split = vec.split_first() 384 assert split == Some((1, [2, 3])) 385 386 vec: Vec[int] = Vec() 387 split = vec.split_first() 388 assert split.is_none() 389 ``` 390 """ 391 if not self._ptr: 392 return _option.NOTHING 393 394 # optimized to only one element in the vector. 395 if self.len() == 1: 396 return _option.Some((self[0], ())) 397 398 first, *rest = self._ptr 399 return _option.Some((first, rest)) 400 401 def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 402 """Split the last and rest elements of the vector, If empty, `None` is returned. 403 404 Example 405 ------- 406 ```py 407 vec = Vec([1, 2, 3]) 408 last, rest = vec.split_last().unwrap() 409 assert (last, rest) == [3, [1, 2]] 410 ``` 411 """ 412 if not self._ptr: 413 return _option.NOTHING 414 415 # optimized to only one element in the vector. 416 if self.len() == 1: 417 return _option.Some((self[0], ())) 418 419 last, *rest = self._ptr[-1], *self._ptr[:-1] 420 return _option.Some((last, rest)) 421 422 def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]: 423 """Divide `self` into two at an index. 424 425 The first will contain all elements from `[0:mid]` excluding `mid` it self. 426 and the second will contain the remaining elements. 427 428 if `mid` > `self.len()`, Then all elements will be moved to the left, 429 returning an empty vec in right. 430 431 Example 432 ------- 433 ```py 434 buffer = Vec((1, 2, 3, 4)) 435 left, right = buffer.split_at(0) 436 assert left == [] and right == [1, 2, 3, 4] 437 438 left, right = buffer.split_at(2) 439 assert left == [1, 2] and right == [2, 3] 440 ``` 441 442 The is roughly the implementation 443 ```py 444 vec[0:mid], vec[mid:] 445 ``` 446 """ 447 return self[0:mid], self[mid:] 448 449 def swap(self, a: int, b: int): 450 """Swap two elements in the vec. 451 452 if `a` equals to `b` then it's guaranteed that elements won't change value. 453 454 Example 455 ------- 456 ```py 457 buf = Vec([1, 2, 3, 4]) 458 buf.swap(0, 3) 459 assert buf == [4, 2, 3, 1] 460 ``` 461 462 Raises 463 ------ 464 IndexError 465 If the positions of `a` or `b` are out of index. 466 """ 467 if self[a] == self[b]: 468 return 469 470 self[a], self[b] = self[b], self[a] 471 472 def swap_unchecked(self, a: int, b: int): 473 """Swap two elements in the vec. without checking if `a` == `b`. 474 475 If you care about `a` and `b` equality, see `Vec.swap`. 476 477 Example 478 ------- 479 ```py 480 buf = Vec([1, 2, 3, 1]) 481 buf.swap_unchecked(0, 3) 482 assert buf == [1, 2, 3, 1] 483 ``` 484 485 Raises 486 ------ 487 IndexError 488 If the positions of `a` or `b` are out of index. 489 """ 490 self[a], self[b] = self[b], self[a] 491 492 def first(self) -> _option.Option[T]: 493 """Get the first element in this vec, returning `None` if there's none. 494 495 Example 496 ------- 497 ```py 498 vec = Vec((1,2,3)) 499 first = vec.first() 500 assert ~first == 1 501 ``` 502 """ 503 return self.get(0) 504 505 def last(self) -> _option.Option[T]: 506 """Get the last element in this vec, returning `None` if there's none. 507 508 Example 509 ------- 510 ```py 511 vec = Vec([1, 2, 3, 4]) 512 last = vec.last() 513 assert ~last == 4 514 ``` 515 """ 516 return self.get(-1) 517 518 def truncate(self, size: int) -> None: 519 """Shortens the vec, keeping the first `size` elements and dropping the rest. 520 521 Example 522 ------- 523 ```py 524 vec = Vec([1,2,3]) 525 vec.truncate(1) 526 assert vec == [1] 527 ``` 528 """ 529 if not self._ptr: 530 return 531 532 del self._ptr[size:] 533 534 def retain(self, f: collections.Callable[[T], bool]) -> None: 535 """Retains only the elements specified by the predicate. 536 537 This operation occurs in-place without copying the original list. 538 539 Example 540 ------- 541 ```py 542 vec = Vec([1, 2, 3]) 543 vec.retain(lambda elem: elem > 1) 544 545 assert vec == [2, 3] 546 ``` 547 548 The impl for this method is very simple 549 ```py 550 for idx, e in enumerate(vec): 551 if not f(e): 552 del vec[idx] 553 ``` 554 """ 555 if not self._ptr: 556 return 557 558 idx = 0 559 while idx < len(self._ptr): 560 if not f(self._ptr[idx]): 561 del self._ptr[idx] 562 else: 563 idx += 1 564 565 def swap_remove(self, item: T) -> T: 566 """Remove the first appearance of `item` from this vector and return it. 567 568 Raises 569 ------ 570 * `ValueError`: if `item` is not in this vector. 571 * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it. 572 573 Example 574 ------- 575 ```py 576 vec = Vec(('a', 'b', 'c')) 577 element = vec.swap_remove('a') 578 assert vec == ['b', 'c'] and element == 'a' 579 ``` 580 """ 581 if self._ptr is None: 582 raise MemoryError("Vec is unallocated.") from None 583 584 return self._ptr.pop(self.index(item)) 585 586 def fill(self, value: T) -> None: 587 """Fill `self` with the given `value`. 588 589 Nothing happens if the vec is empty or unallocated. 590 591 Example 592 ------- 593 ```py 594 a = Vec([0, 1, 2, 3]) 595 a.fill(0) 596 assert a == [0, 0, 0, 0] 597 ``` 598 """ 599 ptr = self._ptr 600 if not ptr: 601 return 602 603 ptr[:] = [value] * len(ptr) 604 605 def push(self, item: T) -> None: 606 """Push an element at the end of the vector. 607 608 Example 609 ------- 610 ```py 611 vec = Vec() 612 vec.push(1) 613 614 assert vec == [1] 615 ``` 616 """ 617 if self._capacity is not None: 618 self.push_within_capacity(item) 619 return 620 621 if self._ptr is None: 622 self._ptr = [] 623 624 self._ptr.append(item) 625 626 def push_within_capacity(self, x: T) -> Result[None, T]: 627 """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element. 628 629 Example 630 ------- 631 ```py 632 vec: Vec[int] = Vec.with_capacity(3) 633 for i in range(3): 634 match vec.push_within_capacity(i): 635 case Ok(_): 636 print("All good.") 637 case Err(elem): 638 print("Reached max cap :< can't push", elem) 639 ``` 640 641 Or you can also just call `Vec.push` and it will push if there's is sufficient capacity. 642 ```py 643 vec: Vec[int] = Vec.with_capacity(3) 644 645 vec.extend((1, 2, 3)) 646 vec.push(4) 647 648 assert vec.len() == 3 649 ``` 650 """ 651 if self._ptr is None: 652 self._ptr = [] 653 654 if self.len() == self._capacity: 655 return _result.Err(x) 656 657 self._ptr.append(x) 658 return _result.Ok(None) 659 660 def reserve(self, additional: int) -> None: 661 """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. 662 663 Example 664 ------- 665 ```py 666 vec = Vec.with_capacity(3) 667 668 for i in range(3): 669 vec.push(i) 670 671 match vec.push_within_capacity(4): 672 case Ok(_): 673 print("Pushed 4 successfully.") 674 case Err(elem): 675 vec.reserve(1) # Reserve capacity for 1 more element. 676 vec.push(4) # Now we can push 4. 677 ``` 678 """ 679 if self._capacity is not None: 680 self._capacity += additional 681 682 def shrink_to_fit(self) -> None: 683 """Shrink the capacity of this `Vec` to match its length. 684 685 If `self` is initialized with no capacity, This is a `NOP`. 686 687 Example 688 ------- 689 ```py 690 s = Vec([1, 2, 3, 4]) 691 692 s.reserve(100) 693 s.shrink_to_fit() 694 assert s.capacity() == 4 695 ``` 696 """ 697 if self._capacity is None: 698 return 699 700 # The capacity is never less than the length. 701 self._capacity = min(self.__len__(), self._capacity) 702 703 def shrink_to(self, min_capacity: int) -> None: 704 """Shrink the capacity of this `Vec` to a minimum specified capacity. 705 706 If `self` is initialized with no capacity or the current capacity is less than the lower limit, 707 This is a `NOP`. 708 709 Example 710 ------- 711 ```py 712 vec = Vec.with_capacity(10) 713 vec.extend([1, 2, 3]) 714 assert vec.capacity() >= 10 715 vec.shrink_to(4) 716 assert vec.capacity() >= 4 717 vec.shrink_to(0) 718 ``` 719 """ 720 if self._capacity is None or self._capacity <= min_capacity: 721 return 722 723 # Ensure the capacity is not reduced below the current length of the vector. 724 self._capacity = max(self.__len__(), min_capacity) 725 726 ########################## 727 # * Builtin Operations * 728 ########################## 729 730 def append(self, value: T) -> None: 731 """An alias to `Vec.push`.""" 732 self.push(value) 733 734 def get(self, index: int) -> _option.Option[T]: 735 """Get the item at the given index, or `Some[None]` if its out of bounds. 736 737 Example 738 ------- 739 ```py 740 vec = Vec((1, 2, 3)) 741 vec.get(0) == Some(1) 742 vec.get(3) == Some(None) 743 ``` 744 """ 745 try: 746 return _option.Some(self[index]) 747 except IndexError: 748 return _option.NOTHING 749 750 def insert(self, index: int, value: T) -> None: 751 """Insert an element at the position `index`. 752 753 Example 754 -------- 755 ```py 756 vec = Vec((2, 3)) 757 vec.insert(0, 1) 758 assert vec == [1, 2, 3] 759 ``` 760 """ 761 self.__setitem__(index, value) 762 763 def pop(self, index: int = -1) -> _option.Option[T]: 764 """Removes the last element from a vector and returns it, or `None` if it is empty. 765 766 Example 767 ------- 768 ```py 769 vec = Vec((1, 2, 3)) 770 assert vec.pop() == Some(3) 771 assert vec == [1, 2] 772 ``` 773 """ 774 if not self._ptr: 775 return _option.NOTHING 776 777 return _option.Some(self._ptr.pop(index)) 778 779 def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]: 780 """Removes the last element from a vector and returns it if `f` returns `True`, 781 or `None` if it is empty. 782 783 Example 784 ------- 785 ```py 786 vec = Vec((1, 2, 3)) 787 assert vec.pop_if(lambda num: num * 2 == 6) == Some(3) 788 assert vec == [1, 2] 789 ``` 790 """ 791 if not self._ptr: 792 return _option.NOTHING 793 794 if pred(self[-1]): 795 return self.pop() 796 797 return _option.NOTHING 798 799 def dedup(self) -> None: 800 """Removes consecutive repeated elements in the vector according to their `__eq__` 801 implementation. 802 803 Example 804 ------- 805 ```py 806 vec = Vec([1, 2, 2, 3, 2]) 807 vec.dedup() 808 assert vec == [1, 2, 3, 2] 809 """ 810 self.dedup_by(lambda a, b: a == b) 811 812 def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None: 813 """Removes all but the first of consecutive elements in the vector satisfying a given equality. 814 815 Example 816 ------- 817 ```py 818 vec = Vec(["foo", "bar", "Bar", "baz", "bar"]) 819 vec.dedup_by(lambda a, b: a.lower() == b.lower()) 820 assert vec == ["foo", "bar", "baz", "bar"] 821 ``` 822 823 Only remove consecutive duplicates. 824 ```py 825 vec = Vec([1, 2, 2, 3, 4, 1]) 826 vec.dedup_by(lambda a, b: a == b) 827 # The final 1 is not adjacent to the first 1, so it is not removed. 828 assert vec == [1, 2, 3, 4, 1] 829 ``` 830 """ 831 832 if not self._ptr or (len_ := len(self._ptr)) <= 1: 833 return 834 835 idx = 1 836 while idx < len_: 837 if same_bucket(self._ptr[idx], self._ptr[idx - 1]): 838 del self._ptr[idx] 839 len_ -= 1 840 else: 841 idx += 1 842 843 def remove(self, item: T) -> None: 844 """Remove the first appearance of `item` from this vector. 845 846 Example 847 ------- 848 ```py 849 vec = Vec(('a', 'b', 'c')) 850 vec.remove('a') 851 assert vec == ['b', 'c'] 852 ``` 853 """ 854 if not self._ptr: 855 return 856 857 self._ptr.remove(item) 858 859 def extend(self, iterable: collections.Iterable[T]) -> None: 860 """Extend this vector from another iterable. 861 862 Example 863 ------- 864 ```py 865 vec = Vec((1, 2, 3)) 866 vec.extend((4, 5, 6)) 867 868 assert vec == [1, 2, 3, 4, 5, 6] 869 ``` 870 """ 871 if self._ptr is None: 872 self._ptr = [] 873 874 self._ptr.extend(iterable) 875 876 def copy(self) -> Vec[T]: 877 """Copy all elements of `self` into a new vector. 878 879 Example 880 ------- 881 ```py 882 original = Vec((1,2,3)) 883 copy = original.copy() 884 copy.push(4) 885 886 print(original) # [1, 2, 3] 887 ``` 888 """ 889 return Vec(self._ptr[:]) if self._ptr else Vec() 890 891 def clear(self) -> None: 892 """Clear all elements of this vector. 893 894 Example 895 ------- 896 ```py 897 vec = Vec((1,2,3)) 898 vec.clear() 899 assert vec.len() == 0 900 ``` 901 """ 902 if not self._ptr: 903 return 904 905 self._ptr.clear() 906 907 def sort( 908 self, 909 *, 910 key: collections.Callable[[T], SupportsRichComparison] | None = None, 911 reverse: bool = False, 912 ) -> None: 913 """This method sorts the list in place, using only < comparisons between items. 914 915 Example 916 ------- 917 ```py 918 vec = Vec((2, 1, 3)) 919 vec.sort() 920 assert vec == [1, 2, 3] 921 ``` 922 """ 923 if not self._ptr: 924 return 925 926 # key can be `None` here just fine, idk why pyright is complaining. 927 self._ptr.sort(key=key, reverse=reverse) # pyright: ignore 928 929 def index( 930 self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize 931 ) -> int: 932 # << Official documentation >> 933 """Return zero-based index in the vec of the first item whose value is equal to `item`. 934 Raises a ValueError if there is no such item. 935 936 Example 937 ------- 938 ```py 939 vec = Vec((1, 2, 3)) 940 assert vec.index(2) == 1 941 ``` 942 """ 943 if self._ptr is None: 944 raise ValueError from None 945 946 return self._ptr.index(item, start, end) 947 948 def count(self, item: T) -> int: 949 """Return the number of occurrences of `item` in the vec. 950 951 `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found. 952 953 Example 954 -------- 955 ```py 956 vec = Vec((1, 2, 3, 3)) 957 assert vec.count(3) == 2 958 ``` 959 """ 960 if self._ptr is None: 961 return 0 962 963 return self._ptr.count(item) 964 965 def __len__(self) -> int: 966 return len(self._ptr) if self._ptr else 0 967 968 def __setitem__(self, index: int, value: T): 969 if not self._ptr: 970 raise IndexError from None 971 972 self._ptr[index] = value 973 974 @typing.overload 975 def __getitem__(self, index: slice) -> Vec[T]: ... 976 977 @typing.overload 978 def __getitem__(self, index: int) -> T: ... 979 980 def __getitem__(self, index: int | slice) -> T | Vec[T]: 981 if not self._ptr: 982 raise IndexError("Index out of range") 983 984 if isinstance(index, slice): 985 return Vec(self._ptr[index]) 986 987 return self._ptr[index] 988 989 def __delitem__(self, index: int) -> None: 990 if not self._ptr: 991 return 992 993 del self._ptr[index] 994 995 def __contains__(self, element: T) -> bool: 996 return element in self._ptr if self._ptr else False 997 998 def __iter__(self) -> collections.Iterator[T]: 999 if self._ptr is None: 1000 return iter(()) 1001 1002 return self._ptr.__iter__() 1003 1004 def __repr__(self) -> str: 1005 return "[]" if not self._ptr else repr(self._ptr) 1006 1007 def __eq__(self, other: Vec[T] | list[T]) -> bool: 1008 if isinstance(other, Vec): 1009 return self._ptr == other._ptr 1010 1011 return self._ptr == other 1012 1013 def __ne__(self, other: Vec[T] | list[T]) -> bool: 1014 return not self.__eq__(other) 1015 1016 def __le__(self, other: list[T]) -> bool: 1017 if not self._ptr: 1018 return False 1019 1020 return self._ptr <= other 1021 1022 def __ge__(self, other: list[T]) -> bool: 1023 if not self._ptr: 1024 return False 1025 1026 return self._ptr >= other 1027 1028 def __lt__(self, other: list[T]) -> bool: 1029 if not self._ptr: 1030 return False 1031 1032 return self._ptr < other 1033 1034 def __gt__(self, other: list[T]) -> bool: 1035 if not self._ptr: 1036 return False 1037 1038 return self._ptr > other 1039 1040 def __bool__(self) -> bool: 1041 return bool(self._ptr) 1042 1043 def __reversed__(self) -> collections.Iterator[T]: 1044 return reversed(self._ptr or ())
A contiguous growable alternative to the builtin list
with extra functionalities.
The layout of Vec<T>
is exactly the same as list<T>
. Which means list<T>
's methods are inherited into Vec<T>
.
Example
names = Vec()
names.push('foo')
names.push('bar')
print(names) # ['foo', 'bar']
assert names.len() == 2
Constructing
Vec()
: Create an empty vec, this will not allocate the initial buffer.Vec(other_list)
: Create a vec which points toother_list
Vec([1, 2, 3])
: Create a vec with[1, 2, 3]
pre-allocated.Vec(iterable)
: Create a vec from aniterable
, This isO(n)
wheren
is the number of elements, since it copiesiterable
's elements into a new list.Vec.with_capacity(5)
: Create a vec that can hold up to 5 elements
Iterating over Vec
There're two ways to iterate over a Vec
. The first is to normally use for
loop.
for i in names:
print(i)
# foo
# bar
The second is to use Vec.iter
, which yields all items in this Vec
from start to end.
Then the iterator gets exhausted as usual, See sain.Iterator
.
iterator = names.iter()
for name in iterator.map(str.upper):
print(name)
# FOO
# BAR
# No more items, The actual vec is left unchanged.
assert iterator.next().is_none()
Comparison operators
A Vec
may be compared with another Vec
or a list
, any other type will return False
vec = Vec([1, 2, 3])
assert vec == [1, 2, 3] # True
assert vec != (1, 2, 3)
Zero-Copy
A vec that gets initialized from a list
will point to it and doesn't copy it.
So any element that gets appended to the list will also get pushed into the vec
that's pointing to it and vice-versa.
cells: list[str] = []
vec = Vec(cells) # This DOES NOT copy the `cells`.
cells.append("foo")
vec[0] == "foo" # True
If you want an owned Vec
that doesn't point to the original list,
copy the list into a new Vec
.
from sain import Vec
# Copy `cells` into a vec.
vec = Vec(cells[:])
cells.append("bar")
vec[1] # IndexError: "bar" doesn't exist in vec.
Implementations
This class implements Vec in Rust.
169 def __init__(self, iterable: collections.Iterable[T] | None = None) -> None: 170 """Create an empty `Vec<T>`. 171 172 The vector will not allocate until elements are pushed onto it. 173 174 Example 175 ------ 176 ```py 177 vec: Vec[float] = Vec() 178 ``` 179 """ 180 # We won't allocate to build the list here. 181 # Instead, On first push. 182 if isinstance(iterable, list): 183 # Calling `list()` on another list will copy it, So instead we just point to it. 184 self._ptr = iterable 185 elif isinstance(iterable, Vec): 186 self._ptr = iterable._ptr 187 # any other iterable that ain't a list needs to get copied into a new list. 188 else: 189 self._ptr: list[T] | None = list(iterable) if iterable else None 190 191 self._capacity: int | None = None
Create an empty Vec<T>
.
The vector will not allocate until elements are pushed onto it.
Example
vec: Vec[float] = Vec()
193 @classmethod 194 def with_capacity(cls, capacity: int) -> Vec[T]: 195 """Create a new `Vec` with at least the specified capacity. 196 This vec will be able to hold `capacity` elements without pushing further. 197 198 The capacity may dynamically grow if `Vec.reserve` is called at runtime. 199 200 Check out `Vec.push_within_capacity` as well. 201 202 Example 203 ------- 204 ```py 205 vec = Vec.with_capacity(3) 206 assert vec.len() == 0 and vec.capacity() >= 3 207 208 vec.push(1) 209 vec.push(2) 210 vec.push(3) 211 print(vec.len()) # 3 212 213 # This won't push. 214 vec.push(4) 215 ``` 216 """ 217 v = cls() 218 v._capacity = capacity 219 return v
Create a new Vec
with at least the specified capacity.
This vec will be able to hold capacity
elements without pushing further.
The capacity may dynamically grow if Vec.reserve
is called at runtime.
Check out Vec.push_within_capacity
as well.
Example
vec = Vec.with_capacity(3)
assert vec.len() == 0 and vec.capacity() >= 3
vec.push(1)
vec.push(2)
vec.push(3)
print(vec.len()) # 3
# This won't push.
vec.push(4)
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())
239 def as_mut(self) -> SliceMut[T]: 240 """Return a mutable view over this vector elements. 241 242 No copying occurs. 243 244 Example 245 ------- 246 ```py 247 def read_bytes(buf: SliceMut[int]) -> None: 248 # similar to how `Write::write_to_end` requires a `&mut [u8]` 249 socket.read_to_end(buf) 250 251 buf: Vec[int] = Vec() 252 read_bytes(buf.as_mut()) # or just `buf` 253 assert vec.len() >= 1 254 ``` 255 """ 256 return SliceMut(self)
Return a mutable view over this vector elements.
No copying occurs.
Example
def read_bytes(buf: SliceMut[int]) -> None:
# similar to how `Write::write_to_end` requires a `&mut [u8]`
socket.read_to_end(buf)
buf: Vec[int] = Vec()
read_bytes(buf.as_mut()) # or just `buf`
assert vec.len() >= 1
258 def len(self) -> int: 259 """Return the number of elements in this vector. 260 261 Example 262 ------- 263 ```py 264 vec = Vec((1,2,3)) 265 266 assert vec.len() == 3 267 ``` 268 """ 269 return self.__len__()
Return the number of elements in this vector.
Example
vec = Vec((1,2,3))
assert vec.len() == 3
271 def capacity(self) -> int: 272 """Return the capacity of this vector if set, 0 if not . 273 274 The number `0` here has two different Meanings: 275 - The first means the `Vec` doesn't have a specific capacity set. 276 - The second means the `Vec` literally initialized with `0` capacity. 277 278 Example 279 ------- 280 ```py 281 vec_with_cap = Vec.with_capacity(3) 282 assert vec_with_cap.capacity().unwrap() == 3 283 284 vec = Vec([1, 2, 3]) 285 assert vec.capacity() == 0 286 ``` 287 """ 288 return 0 if self._capacity is None else self._capacity
Return the capacity of this vector if set, 0 if not .
The number 0
here has two different Meanings:
- The first means the
Vec
doesn't have a specific capacity set. - The second means the
Vec
literally initialized with0
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
290 def iter(self) -> _iter.TrustedIter[T]: 291 """Returns an iterator over this vector elements. 292 293 Example 294 ------- 295 ```py 296 vec = Vec([1 ,2, 3]) 297 for element in vec.iter(): 298 print(element) 299 ``` 300 """ 301 return _iter.TrustedIter(self._ptr or ())
Returns an iterator over this vector elements.
Example
vec = Vec([1 ,2, 3])
for element in vec.iter():
print(element)
303 def is_empty(self) -> bool: 304 """Returns true if the vector contains no elements. 305 306 Inlined into: 307 ```py 308 not self.vec 309 ``` 310 """ 311 return not self._ptr
Returns true if the vector contains no elements.
Inlined into:
not self.vec
313 def leak(self) -> list[T]: 314 """Consumes and leaks the Vec, returning a mutable reference to the contents. 315 316 After calling this, this vec will no longer reference the underlying buffer, 317 therefore, it becomes unusable. 318 319 if `self` is uninitialized, an empty list is returned. 320 321 This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without 322 any copies. 323 324 Example 325 ------- 326 ```py 327 x = Vec([1, 2, 3]) 328 owned = x.leak() 329 # x is now unusable. 330 owned[0] += 1 331 assert owned == [2, 2, 3] 332 ``` 333 """ 334 if self._ptr is not None: 335 # don't point to `_ptr` anymore. 336 tmp = self._ptr 337 del self._ptr 338 return tmp 339 340 return []
Consumes and leaks the Vec, returning a mutable reference to the contents.
After calling this, this vec will no longer reference the underlying buffer, therefore, it becomes unusable.
if self
is uninitialized, an empty list is returned.
This function is only useful when you want to detach from a Vec<T>
and get a list[T]
without
any copies.
Example
x = Vec([1, 2, 3])
owned = x.leak()
# x is now unusable.
owned[0] += 1
assert owned == [2, 2, 3]
342 def split_off(self, at: int) -> Vec[T]: 343 """Split the vector off at the specified position, returning a new 344 vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`. 345 346 if this vec is empty, `self` is returned unchanged. 347 348 Example 349 ------- 350 ```py 351 origin = Vec((1, 2, 3, 4)) 352 split = vec.split_off(2) 353 354 print(origin, split) # [1, 2], [3, 4] 355 ``` 356 357 Raises 358 ------ 359 `IndexError` 360 This method will raise if `at` > `len(self)` 361 """ 362 len_ = self.len() 363 if at > len_: 364 raise IndexError( 365 f"Index `at` ({at}) should be <= than len of vector ({len_}) " 366 ) from None 367 368 # Either the list is empty or uninit. 369 if not self._ptr: 370 return self 371 372 split = self[at:len_] # split the items into a new vec. 373 del self._ptr[at:len_] # remove the items from the original list. 374 return split
Split the vector off at the specified position, returning a new
vec at the range of [at : len]
, leaving self
at [at : vec_len]
.
if this vec is empty, self
is returned unchanged.
Example
origin = Vec((1, 2, 3, 4))
split = vec.split_off(2)
print(origin, split) # [1, 2], [3, 4]
Raises
IndexError
: This method will raise ifat
>len(self)
376 def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 377 """Split the first and rest elements of the vector, If empty, `None` is returned. 378 379 Example 380 ------- 381 ```py 382 vec = Vec([1, 2, 3]) 383 split = vec.split_first() 384 assert split == Some((1, [2, 3])) 385 386 vec: Vec[int] = Vec() 387 split = vec.split_first() 388 assert split.is_none() 389 ``` 390 """ 391 if not self._ptr: 392 return _option.NOTHING 393 394 # optimized to only one element in the vector. 395 if self.len() == 1: 396 return _option.Some((self[0], ())) 397 398 first, *rest = self._ptr 399 return _option.Some((first, rest))
Split the first and rest elements of the vector, If empty, None
is returned.
Example
vec = Vec([1, 2, 3])
split = vec.split_first()
assert split == Some((1, [2, 3]))
vec: Vec[int] = Vec()
split = vec.split_first()
assert split.is_none()
401 def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 402 """Split the last and rest elements of the vector, If empty, `None` is returned. 403 404 Example 405 ------- 406 ```py 407 vec = Vec([1, 2, 3]) 408 last, rest = vec.split_last().unwrap() 409 assert (last, rest) == [3, [1, 2]] 410 ``` 411 """ 412 if not self._ptr: 413 return _option.NOTHING 414 415 # optimized to only one element in the vector. 416 if self.len() == 1: 417 return _option.Some((self[0], ())) 418 419 last, *rest = self._ptr[-1], *self._ptr[:-1] 420 return _option.Some((last, rest))
Split the last and rest elements of the vector, If empty, None
is returned.
Example
vec = Vec([1, 2, 3])
last, rest = vec.split_last().unwrap()
assert (last, rest) == [3, [1, 2]]
422 def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]: 423 """Divide `self` into two at an index. 424 425 The first will contain all elements from `[0:mid]` excluding `mid` it self. 426 and the second will contain the remaining elements. 427 428 if `mid` > `self.len()`, Then all elements will be moved to the left, 429 returning an empty vec in right. 430 431 Example 432 ------- 433 ```py 434 buffer = Vec((1, 2, 3, 4)) 435 left, right = buffer.split_at(0) 436 assert left == [] and right == [1, 2, 3, 4] 437 438 left, right = buffer.split_at(2) 439 assert left == [1, 2] and right == [2, 3] 440 ``` 441 442 The is roughly the implementation 443 ```py 444 vec[0:mid], vec[mid:] 445 ``` 446 """ 447 return self[0:mid], self[mid:]
Divide self
into two at an index.
The first will contain all elements from [0:mid]
excluding mid
it self.
and the second will contain the remaining elements.
if mid
> self.len()
, Then all elements will be moved to the left,
returning an empty vec in right.
Example
buffer = Vec((1, 2, 3, 4))
left, right = buffer.split_at(0)
assert left == [] and right == [1, 2, 3, 4]
left, right = buffer.split_at(2)
assert left == [1, 2] and right == [2, 3]
The is roughly the implementation
vec[0:mid], vec[mid:]
449 def swap(self, a: int, b: int): 450 """Swap two elements in the vec. 451 452 if `a` equals to `b` then it's guaranteed that elements won't change value. 453 454 Example 455 ------- 456 ```py 457 buf = Vec([1, 2, 3, 4]) 458 buf.swap(0, 3) 459 assert buf == [4, 2, 3, 1] 460 ``` 461 462 Raises 463 ------ 464 IndexError 465 If the positions of `a` or `b` are out of index. 466 """ 467 if self[a] == self[b]: 468 return 469 470 self[a], self[b] = self[b], self[a]
Swap two elements in the vec.
if a
equals to b
then it's guaranteed that elements won't change value.
Example
buf = Vec([1, 2, 3, 4])
buf.swap(0, 3)
assert buf == [4, 2, 3, 1]
Raises
- IndexError: If the positions of
a
orb
are out of index.
472 def swap_unchecked(self, a: int, b: int): 473 """Swap two elements in the vec. without checking if `a` == `b`. 474 475 If you care about `a` and `b` equality, see `Vec.swap`. 476 477 Example 478 ------- 479 ```py 480 buf = Vec([1, 2, 3, 1]) 481 buf.swap_unchecked(0, 3) 482 assert buf == [1, 2, 3, 1] 483 ``` 484 485 Raises 486 ------ 487 IndexError 488 If the positions of `a` or `b` are out of index. 489 """ 490 self[a], self[b] = self[b], self[a]
Swap two elements in the vec. without checking if a
== b
.
If you care about a
and b
equality, see Vec.swap
.
Example
buf = Vec([1, 2, 3, 1])
buf.swap_unchecked(0, 3)
assert buf == [1, 2, 3, 1]
Raises
- IndexError: If the positions of
a
orb
are out of index.
492 def first(self) -> _option.Option[T]: 493 """Get the first element in this vec, returning `None` if there's none. 494 495 Example 496 ------- 497 ```py 498 vec = Vec((1,2,3)) 499 first = vec.first() 500 assert ~first == 1 501 ``` 502 """ 503 return self.get(0)
Get the first element in this vec, returning None
if there's none.
Example
vec = Vec((1,2,3))
first = vec.first()
assert ~first == 1
505 def last(self) -> _option.Option[T]: 506 """Get the last element in this vec, returning `None` if there's none. 507 508 Example 509 ------- 510 ```py 511 vec = Vec([1, 2, 3, 4]) 512 last = vec.last() 513 assert ~last == 4 514 ``` 515 """ 516 return self.get(-1)
Get the last element in this vec, returning None
if there's none.
Example
vec = Vec([1, 2, 3, 4])
last = vec.last()
assert ~last == 4
518 def truncate(self, size: int) -> None: 519 """Shortens the vec, keeping the first `size` elements and dropping the rest. 520 521 Example 522 ------- 523 ```py 524 vec = Vec([1,2,3]) 525 vec.truncate(1) 526 assert vec == [1] 527 ``` 528 """ 529 if not self._ptr: 530 return 531 532 del self._ptr[size:]
Shortens the vec, keeping the first size
elements and dropping the rest.
Example
vec = Vec([1,2,3])
vec.truncate(1)
assert vec == [1]
534 def retain(self, f: collections.Callable[[T], bool]) -> None: 535 """Retains only the elements specified by the predicate. 536 537 This operation occurs in-place without copying the original list. 538 539 Example 540 ------- 541 ```py 542 vec = Vec([1, 2, 3]) 543 vec.retain(lambda elem: elem > 1) 544 545 assert vec == [2, 3] 546 ``` 547 548 The impl for this method is very simple 549 ```py 550 for idx, e in enumerate(vec): 551 if not f(e): 552 del vec[idx] 553 ``` 554 """ 555 if not self._ptr: 556 return 557 558 idx = 0 559 while idx < len(self._ptr): 560 if not f(self._ptr[idx]): 561 del self._ptr[idx] 562 else: 563 idx += 1
Retains only the elements specified by the predicate.
This operation occurs in-place without copying the original list.
Example
vec = Vec([1, 2, 3])
vec.retain(lambda elem: elem > 1)
assert vec == [2, 3]
The impl for this method is very simple
for idx, e in enumerate(vec):
if not f(e):
del vec[idx]
565 def swap_remove(self, item: T) -> T: 566 """Remove the first appearance of `item` from this vector and return it. 567 568 Raises 569 ------ 570 * `ValueError`: if `item` is not in this vector. 571 * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it. 572 573 Example 574 ------- 575 ```py 576 vec = Vec(('a', 'b', 'c')) 577 element = vec.swap_remove('a') 578 assert vec == ['b', 'c'] and element == 'a' 579 ``` 580 """ 581 if self._ptr is None: 582 raise MemoryError("Vec is unallocated.") from None 583 584 return self._ptr.pop(self.index(item))
Remove the first appearance of item
from this vector and return it.
Raises
*
ValueError
(ifitem
is not in this vector.):*
MemoryError
(if this vector hasn't allocated, Aka nothing has been pushed to it.):
Example
vec = Vec(('a', 'b', 'c'))
element = vec.swap_remove('a')
assert vec == ['b', 'c'] and element == 'a'
586 def fill(self, value: T) -> None: 587 """Fill `self` with the given `value`. 588 589 Nothing happens if the vec is empty or unallocated. 590 591 Example 592 ------- 593 ```py 594 a = Vec([0, 1, 2, 3]) 595 a.fill(0) 596 assert a == [0, 0, 0, 0] 597 ``` 598 """ 599 ptr = self._ptr 600 if not ptr: 601 return 602 603 ptr[:] = [value] * len(ptr)
Fill self
with the given value
.
Nothing happens if the vec is empty or unallocated.
Example
a = Vec([0, 1, 2, 3])
a.fill(0)
assert a == [0, 0, 0, 0]
605 def push(self, item: T) -> None: 606 """Push an element at the end of the vector. 607 608 Example 609 ------- 610 ```py 611 vec = Vec() 612 vec.push(1) 613 614 assert vec == [1] 615 ``` 616 """ 617 if self._capacity is not None: 618 self.push_within_capacity(item) 619 return 620 621 if self._ptr is None: 622 self._ptr = [] 623 624 self._ptr.append(item)
Push an element at the end of the vector.
Example
vec = Vec()
vec.push(1)
assert vec == [1]
626 def push_within_capacity(self, x: T) -> Result[None, T]: 627 """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element. 628 629 Example 630 ------- 631 ```py 632 vec: Vec[int] = Vec.with_capacity(3) 633 for i in range(3): 634 match vec.push_within_capacity(i): 635 case Ok(_): 636 print("All good.") 637 case Err(elem): 638 print("Reached max cap :< can't push", elem) 639 ``` 640 641 Or you can also just call `Vec.push` and it will push if there's is sufficient capacity. 642 ```py 643 vec: Vec[int] = Vec.with_capacity(3) 644 645 vec.extend((1, 2, 3)) 646 vec.push(4) 647 648 assert vec.len() == 3 649 ``` 650 """ 651 if self._ptr is None: 652 self._ptr = [] 653 654 if self.len() == self._capacity: 655 return _result.Err(x) 656 657 self._ptr.append(x) 658 return _result.Ok(None)
Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
Example
vec: Vec[int] = Vec.with_capacity(3)
for i in range(3):
match vec.push_within_capacity(i):
case Ok(_):
print("All good.")
case Err(elem):
print("Reached max cap :< can't push", elem)
Or you can also just call Vec.push
and it will push if there's is sufficient capacity.
vec: Vec[int] = Vec.with_capacity(3)
vec.extend((1, 2, 3))
vec.push(4)
assert vec.len() == 3
660 def reserve(self, additional: int) -> None: 661 """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. 662 663 Example 664 ------- 665 ```py 666 vec = Vec.with_capacity(3) 667 668 for i in range(3): 669 vec.push(i) 670 671 match vec.push_within_capacity(4): 672 case Ok(_): 673 print("Pushed 4 successfully.") 674 case Err(elem): 675 vec.reserve(1) # Reserve capacity for 1 more element. 676 vec.push(4) # Now we can push 4. 677 ``` 678 """ 679 if self._capacity is not None: 680 self._capacity += additional
Reserves capacity for at least additional more elements to be inserted in the given Vec
Example
vec = Vec.with_capacity(3)
for i in range(3):
vec.push(i)
match vec.push_within_capacity(4):
case Ok(_):
print("Pushed 4 successfully.")
case Err(elem):
vec.reserve(1) # Reserve capacity for 1 more element.
vec.push(4) # Now we can push 4.
682 def shrink_to_fit(self) -> None: 683 """Shrink the capacity of this `Vec` to match its length. 684 685 If `self` is initialized with no capacity, This is a `NOP`. 686 687 Example 688 ------- 689 ```py 690 s = Vec([1, 2, 3, 4]) 691 692 s.reserve(100) 693 s.shrink_to_fit() 694 assert s.capacity() == 4 695 ``` 696 """ 697 if self._capacity is None: 698 return 699 700 # The capacity is never less than the length. 701 self._capacity = min(self.__len__(), self._capacity)
Shrink the capacity of this Vec
to match its length.
If self
is initialized with no capacity, This is a NOP
.
Example
s = Vec([1, 2, 3, 4])
s.reserve(100)
s.shrink_to_fit()
assert s.capacity() == 4
703 def shrink_to(self, min_capacity: int) -> None: 704 """Shrink the capacity of this `Vec` to a minimum specified capacity. 705 706 If `self` is initialized with no capacity or the current capacity is less than the lower limit, 707 This is a `NOP`. 708 709 Example 710 ------- 711 ```py 712 vec = Vec.with_capacity(10) 713 vec.extend([1, 2, 3]) 714 assert vec.capacity() >= 10 715 vec.shrink_to(4) 716 assert vec.capacity() >= 4 717 vec.shrink_to(0) 718 ``` 719 """ 720 if self._capacity is None or self._capacity <= min_capacity: 721 return 722 723 # Ensure the capacity is not reduced below the current length of the vector. 724 self._capacity = max(self.__len__(), min_capacity)
Shrink the capacity of this Vec
to a minimum specified capacity.
If self
is initialized with no capacity or the current capacity is less than the lower limit,
This is a NOP
.
Example
vec = Vec.with_capacity(10)
vec.extend([1, 2, 3])
assert vec.capacity() >= 10
vec.shrink_to(4)
assert vec.capacity() >= 4
vec.shrink_to(0)
734 def get(self, index: int) -> _option.Option[T]: 735 """Get the item at the given index, or `Some[None]` if its out of bounds. 736 737 Example 738 ------- 739 ```py 740 vec = Vec((1, 2, 3)) 741 vec.get(0) == Some(1) 742 vec.get(3) == Some(None) 743 ``` 744 """ 745 try: 746 return _option.Some(self[index]) 747 except IndexError: 748 return _option.NOTHING
Get the item at the given index, or Some[None]
if its out of bounds.
Example
vec = Vec((1, 2, 3))
vec.get(0) == Some(1)
vec.get(3) == Some(None)
750 def insert(self, index: int, value: T) -> None: 751 """Insert an element at the position `index`. 752 753 Example 754 -------- 755 ```py 756 vec = Vec((2, 3)) 757 vec.insert(0, 1) 758 assert vec == [1, 2, 3] 759 ``` 760 """ 761 self.__setitem__(index, value)
Insert an element at the position index
.
Example
vec = Vec((2, 3))
vec.insert(0, 1)
assert vec == [1, 2, 3]
763 def pop(self, index: int = -1) -> _option.Option[T]: 764 """Removes the last element from a vector and returns it, or `None` if it is empty. 765 766 Example 767 ------- 768 ```py 769 vec = Vec((1, 2, 3)) 770 assert vec.pop() == Some(3) 771 assert vec == [1, 2] 772 ``` 773 """ 774 if not self._ptr: 775 return _option.NOTHING 776 777 return _option.Some(self._ptr.pop(index))
Removes the last element from a vector and returns it, or None
if it is empty.
Example
vec = Vec((1, 2, 3))
assert vec.pop() == Some(3)
assert vec == [1, 2]
779 def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]: 780 """Removes the last element from a vector and returns it if `f` returns `True`, 781 or `None` if it is empty. 782 783 Example 784 ------- 785 ```py 786 vec = Vec((1, 2, 3)) 787 assert vec.pop_if(lambda num: num * 2 == 6) == Some(3) 788 assert vec == [1, 2] 789 ``` 790 """ 791 if not self._ptr: 792 return _option.NOTHING 793 794 if pred(self[-1]): 795 return self.pop() 796 797 return _option.NOTHING
Removes the last element from a vector and returns it if f
returns True
,
or None
if it is empty.
Example
vec = Vec((1, 2, 3))
assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
assert vec == [1, 2]
799 def dedup(self) -> None: 800 """Removes consecutive repeated elements in the vector according to their `__eq__` 801 implementation. 802 803 Example 804 ------- 805 ```py 806 vec = Vec([1, 2, 2, 3, 2]) 807 vec.dedup() 808 assert vec == [1, 2, 3, 2] 809 """ 810 self.dedup_by(lambda a, b: a == b)
Removes consecutive repeated elements in the vector according to their __eq__
implementation.
Example
```py vec = Vec([1, 2, 2, 3, 2]) vec.dedup() assert vec == [1, 2, 3, 2]
812 def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None: 813 """Removes all but the first of consecutive elements in the vector satisfying a given equality. 814 815 Example 816 ------- 817 ```py 818 vec = Vec(["foo", "bar", "Bar", "baz", "bar"]) 819 vec.dedup_by(lambda a, b: a.lower() == b.lower()) 820 assert vec == ["foo", "bar", "baz", "bar"] 821 ``` 822 823 Only remove consecutive duplicates. 824 ```py 825 vec = Vec([1, 2, 2, 3, 4, 1]) 826 vec.dedup_by(lambda a, b: a == b) 827 # The final 1 is not adjacent to the first 1, so it is not removed. 828 assert vec == [1, 2, 3, 4, 1] 829 ``` 830 """ 831 832 if not self._ptr or (len_ := len(self._ptr)) <= 1: 833 return 834 835 idx = 1 836 while idx < len_: 837 if same_bucket(self._ptr[idx], self._ptr[idx - 1]): 838 del self._ptr[idx] 839 len_ -= 1 840 else: 841 idx += 1
Removes all but the first of consecutive elements in the vector satisfying a given equality.
Example
vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
vec.dedup_by(lambda a, b: a.lower() == b.lower())
assert vec == ["foo", "bar", "baz", "bar"]
Only remove consecutive duplicates.
vec = Vec([1, 2, 2, 3, 4, 1])
vec.dedup_by(lambda a, b: a == b)
# The final 1 is not adjacent to the first 1, so it is not removed.
assert vec == [1, 2, 3, 4, 1]
843 def remove(self, item: T) -> None: 844 """Remove the first appearance of `item` from this vector. 845 846 Example 847 ------- 848 ```py 849 vec = Vec(('a', 'b', 'c')) 850 vec.remove('a') 851 assert vec == ['b', 'c'] 852 ``` 853 """ 854 if not self._ptr: 855 return 856 857 self._ptr.remove(item)
Remove the first appearance of item
from this vector.
Example
vec = Vec(('a', 'b', 'c'))
vec.remove('a')
assert vec == ['b', 'c']
859 def extend(self, iterable: collections.Iterable[T]) -> None: 860 """Extend this vector from another iterable. 861 862 Example 863 ------- 864 ```py 865 vec = Vec((1, 2, 3)) 866 vec.extend((4, 5, 6)) 867 868 assert vec == [1, 2, 3, 4, 5, 6] 869 ``` 870 """ 871 if self._ptr is None: 872 self._ptr = [] 873 874 self._ptr.extend(iterable)
Extend this vector from another iterable.
Example
vec = Vec((1, 2, 3))
vec.extend((4, 5, 6))
assert vec == [1, 2, 3, 4, 5, 6]
876 def copy(self) -> Vec[T]: 877 """Copy all elements of `self` into a new vector. 878 879 Example 880 ------- 881 ```py 882 original = Vec((1,2,3)) 883 copy = original.copy() 884 copy.push(4) 885 886 print(original) # [1, 2, 3] 887 ``` 888 """ 889 return Vec(self._ptr[:]) if self._ptr else Vec()
Copy all elements of self
into a new vector.
Example
original = Vec((1,2,3))
copy = original.copy()
copy.push(4)
print(original) # [1, 2, 3]
891 def clear(self) -> None: 892 """Clear all elements of this vector. 893 894 Example 895 ------- 896 ```py 897 vec = Vec((1,2,3)) 898 vec.clear() 899 assert vec.len() == 0 900 ``` 901 """ 902 if not self._ptr: 903 return 904 905 self._ptr.clear()
Clear all elements of this vector.
Example
vec = Vec((1,2,3))
vec.clear()
assert vec.len() == 0
907 def sort( 908 self, 909 *, 910 key: collections.Callable[[T], SupportsRichComparison] | None = None, 911 reverse: bool = False, 912 ) -> None: 913 """This method sorts the list in place, using only < comparisons between items. 914 915 Example 916 ------- 917 ```py 918 vec = Vec((2, 1, 3)) 919 vec.sort() 920 assert vec == [1, 2, 3] 921 ``` 922 """ 923 if not self._ptr: 924 return 925 926 # key can be `None` here just fine, idk why pyright is complaining. 927 self._ptr.sort(key=key, reverse=reverse) # pyright: ignore
This method sorts the list in place, using only < comparisons between items.
Example
vec = Vec((2, 1, 3))
vec.sort()
assert vec == [1, 2, 3]
929 def index( 930 self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize 931 ) -> int: 932 # << Official documentation >> 933 """Return zero-based index in the vec of the first item whose value is equal to `item`. 934 Raises a ValueError if there is no such item. 935 936 Example 937 ------- 938 ```py 939 vec = Vec((1, 2, 3)) 940 assert vec.index(2) == 1 941 ``` 942 """ 943 if self._ptr is None: 944 raise ValueError from None 945 946 return self._ptr.index(item, start, end)
Return zero-based index in the vec of the first item whose value is equal to item
.
Raises a ValueError if there is no such item.
Example
vec = Vec((1, 2, 3))
assert vec.index(2) == 1
948 def count(self, item: T) -> int: 949 """Return the number of occurrences of `item` in the vec. 950 951 `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found. 952 953 Example 954 -------- 955 ```py 956 vec = Vec((1, 2, 3, 3)) 957 assert vec.count(3) == 2 958 ``` 959 """ 960 if self._ptr is None: 961 return 0 962 963 return self._ptr.count(item)
Return the number of occurrences of item
in the vec.
0
is returned if the vector is empty or hasn't been initialized, as well if them item not found.
Example
vec = Vec((1, 2, 3, 3))
assert vec.count(3) == 2
Inherited Members
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.
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.
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()
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
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.
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()
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
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()
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.
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)
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 genericT
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.
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)
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.
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)
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 genericT
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.
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)
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.
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)
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()