sain
sain is a set of minimal abstraction that brings Rust's ecosystem to Python. It offers a few of the core Rust types like Vec<T>
and Result<T, E>
and more.
1# BSD 3-Clause License 2# 3# Copyright (c) 2022-Present, nxtlo 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31"""sain is a set of minimal abstraction that brings Rust's ecosystem to Python. It offers a few of the core Rust types like `Vec<T>` and `Result<T, E>` and more.""" 32 33from __future__ import annotations 34 35__all__ = ( 36 # cfg.py 37 "cfg", 38 "cfg_attr", 39 # default.py 40 "default", 41 "Default", 42 # option.py 43 "option", 44 "Some", 45 "Option", 46 "NOTHING", 47 # iter.py 48 "Iter", 49 "Iterator", 50 "iter", 51 # macros.py 52 "macros", 53 "todo", 54 "deprecated", 55 "unimplemented", 56 "doc", 57 "include_str", 58 "include_bytes", 59 "assert_eq", 60 "assert_ne", 61 # futures.py 62 "futures", 63 # result.py 64 "result", 65 "Ok", 66 "Err", 67 "Result", 68 # collections 69 "collections", 70 "Vec", 71 # error.py 72 "error", 73 "Error", 74 # boxed.py 75 "boxed", 76 "Box", 77 # sync 78 "sync", 79 # maybe_uninit.py 80 "maybe_uninit", 81 # convert 82 "convert", 83 "From", 84 "TryFrom", 85 "Into", 86 "TryInto", 87 "ToString", 88) 89 90from . import boxed 91from . import collections 92from . import convert 93from . import default 94from . import error 95from . import futures 96from . import iter 97from . import macros 98from . import maybe_uninit 99from . import option 100from . import result 101from . import sync 102from .boxed import Box 103from .cfg import cfg 104from .cfg import cfg_attr 105from .collections import Vec 106from .convert import From 107from .convert import Into 108from .convert import ToString 109from .convert import TryFrom 110from .convert import TryInto 111from .default import Default 112from .error import Error 113from .iter import Iter 114from .iter import Iterator 115from .macros import assert_eq 116from .macros import assert_ne 117from .macros import deprecated 118from .macros import doc 119from .macros import include_bytes 120from .macros import include_str 121from .macros import todo 122from .macros import unimplemented 123from .option import NOTHING 124from .option import Option 125from .option import Some 126from .result import Err 127from .result import Ok 128from .result import Result 129 130__version__: str = "1.3.0" 131__url__: str = "https://github.com/nxtlo/sain" 132__author__: str = "nxtlo" 133__about__: str = ( 134 "Sain is a dependency-free library that implements some of the Rust core" 135 "types which abstracts over common patterns in Rust for Python." 136) 137__license__: str = "BSD 3-Clause License"
173@rustc_diagnostic_item("cfg") 174def cfg( 175 target_os: System | None = None, 176 python_version: tuple[int, int, int] | None = None, 177 target_arch: Arch | None = None, 178 impl: Python | None = None, 179) -> bool: 180 """A function that will run the code only if all predicate attributes returns `True`. 181 182 The difference between this function and `cfg_attr` is that this function will not raise an exception. 183 Instead it will return `False` if any of the attributes fails. 184 185 Example 186 ------- 187 ```py 188 import sain 189 190 if cfg(target_os="windows"): 191 print("Windows") 192 elif cfg(target_os="linux", target_arch="arm64"): 193 print("Linux") 194 else: 195 print("Something else") 196 ``` 197 198 Parameters 199 ---------- 200 target_os : `str | None` 201 The targeted operating system thats required for the object to be ran. 202 python_version : `tuple[int, int, int] | None` 203 The targeted Python version thats required for the object to be ran. Format must be `(3, 9, 5)`` 204 target_arch : `str | None` 205 The CPU targeted architecture thats required for the object to be ran. 206 impl : `str | None` 207 The Python implementation thats required for the object to be ran. 208 209 Returns 210 ------- 211 `bool` 212 The condition that was checked. 213 """ 214 checker = _AttrCheck( 215 lambda: None, 216 no_raise=True, 217 target_os=target_os, 218 python_version=python_version, 219 target_arch=target_arch, 220 impl=impl, 221 ) 222 return checker.internal_check()
A function that will run the code only if all predicate attributes returns True
.
The difference between this function and cfg_attr
is that this function will not raise an exception.
Instead it will return False
if any of the attributes fails.
Example
import sain
if cfg(target_os="windows"):
print("Windows")
elif cfg(target_os="linux", target_arch="arm64"):
print("Linux")
else:
print("Something else")
Parameters
- target_os (
str | None
): The targeted operating system thats required for the object to be ran. - python_version (
tuple[int, int, int] | None
): The targeted Python version thats required for the object to be ran. Format must be `(3, 9, 5)`` - target_arch (
str | None
): The CPU targeted architecture thats required for the object to be ran. - impl (
str | None
): The Python implementation thats required for the object to be ran.
Returns
bool
: The condition that was checked.- # Implementations
- **This function implements cfg:
101@rustc_diagnostic_item("cfg_attr") 102def cfg_attr( 103 *, 104 target_os: System | None = None, 105 python_version: tuple[int, int, int] | None = None, 106 target_arch: Arch | None = None, 107 impl: Python | None = None, 108) -> collections.Callable[[F], F]: 109 """Conditional runtime object configuration based on passed arguments. 110 111 If the decorated object gets called and one of the attributes returns `False`, 112 `RuntimeError` will be raised and the object will not run. 113 114 Example 115 ------- 116 ```py 117 import sain 118 119 @cfg_attr(target_os="windows") 120 def windows_only(): 121 # Do stuff with Windows's API. 122 ... 123 124 # Mut be PyPy Python implementation or `RuntimeError` will be raised 125 # when creating the instance. 126 @cfg_attr(impl="PyPy") 127 class Zoo: 128 @sain.cfg_attr(target_os="linux") 129 def bark(self) -> None: 130 ... 131 132 # An instance will not be created if raised. 133 zoo = Zoo() 134 # RuntimeError("class Zoo requires PyPy implementation") 135 ``` 136 137 Parameters 138 ---------- 139 target_os : `str | None` 140 The targeted operating system thats required for the object. 141 python_version : `tuple[int, int, int] | None` 142 The targeted Python version thats required for the object. Format must be `(3, 9, 5)`. 143 target_arch : `str | None` 144 The CPU targeted architecture thats required for the object. 145 impl : `str | None` 146 The Python implementation thats required for the object. 147 148 Raises 149 ------ 150 `RuntimeError` 151 This fails if any of the attributes returns `False`. 152 `ValueError` 153 If the passed Python implementation is unknown. 154 """ 155 156 def decorator(callback: F) -> F: 157 @functools.wraps(callback) 158 def wrapper(*args: typing.Any, **kwargs: typing.Any) -> F: 159 checker = _AttrCheck( 160 callback, 161 target_os=target_os, 162 python_version=python_version, 163 target_arch=target_arch, 164 impl=impl, 165 ) 166 return checker(*args, **kwargs) 167 168 return typing.cast(F, wrapper) 169 170 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 thats required for the object. - python_version (
tuple[int, int, int] | None
): The targeted Python version thats required for the object. Format must be(3, 9, 5)
. - target_arch (
str | None
): The CPU targeted architecture thats required for the object. - impl (
str | None
): The Python implementation thats required for the object.
Raises
RuntimeError
: This fails if any of the attributes returnsFalse
.ValueError
: If the passed Python implementation is unknown.- # Implementations
- **This function implements cfg_attr:
58@rustc_diagnostic_item("Default") 59@typing.runtime_checkable 60class Default(typing.Protocol[_T_co]): 61 """An interface for an object that has a default value. 62 63 Example 64 ------- 65 ```py 66 from sain import Default 67 68 class Cache(Default[dict[str, Any]]): 69 70 @staticmethod 71 def default() -> dict[str, Any]: 72 return {} 73 74 cache = Cache.default() 75 print(cache) 76 assert isinstance(cache, Default) 77 # {} 78 ``` 79 """ 80 81 __slots__ = () 82 83 @rustc_diagnostic_item("default_fn") 84 @staticmethod 85 def default() -> _T_co: 86 """Return the default value of the object.""" 87 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.
1941def _no_init_or_replace_init(self, *args, **kwargs): 1942 cls = type(self) 1943 1944 if cls._is_protocol: 1945 raise TypeError('Protocols cannot be instantiated') 1946 1947 # Already using a custom `__init__`. No need to calculate correct 1948 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1949 if cls.__init__ is not _no_init_or_replace_init: 1950 return 1951 1952 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1953 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1954 # searches for a proper new `__init__` in the MRO. The new `__init__` 1955 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1956 # instantiation of the protocol subclass will thus use the new 1957 # `__init__` and no longer call `_no_init_or_replace_init`. 1958 for base in cls.__mro__: 1959 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1960 if init is not _no_init_or_replace_init: 1961 cls.__init__ = init 1962 break 1963 else: 1964 # should not happen 1965 cls.__init__ = object.__init__ 1966 1967 cls.__init__(self, *args, **kwargs)
56@rustc_diagnostic_item("Option") 57@typing.final 58class Some(typing.Generic[T], _default.Default["Option[None]"]): 59 """The `Option` type. An object that might be `T` or `None`. 60 61 It is a replacement for `typing.Optional[T]`, with more convenient methods to handle the contained value. 62 63 Example 64 ------- 65 ```py 66 value = Some("Hello") 67 print(value) 68 # Some("Hello") 69 70 # This will unwrap the contained value as long as 71 # it is not `None` otherwise this will raise an error. 72 print(value.unwrap()) 73 # "Hello" 74 75 none_value = Some(None) 76 while none_value.unwrap(): 77 # Never unreachable! 78 79 # Solving it with `unwrap_or` method to unwrap the value or return a default value. 80 print(none_value.unwrap_or(10)) 81 # 10 82 ``` 83 """ 84 85 __slots__ = ("_value",) 86 __match_args__ = ("_value",) 87 88 def __init__(self, value: T | None, /) -> None: 89 self._value = value 90 91 @staticmethod 92 def default() -> Option[None]: 93 """Default value for `Some`. Returns `None` wrapped in `Some`. 94 95 Example 96 ------- 97 ```py 98 assert Some.default() == NOTHING 99 ``` 100 """ 101 return NOTHING 102 103 # *- Reading the value -* 104 105 def transpose(self) -> T | None: 106 """Convert `Option[T]` into `T | None`. 107 108 Examples 109 -------- 110 ```py 111 opt = Some('char') 112 x = opt.transpose() 113 assert x == 'char' 114 115 opt = Some(None) 116 assert opt.transpose() is None 117 ``` 118 """ 119 return self._value 120 121 def unwrap(self) -> T: 122 """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`. 123 124 It's usually not recommended to use this method in production code, since it raises. 125 126 Example 127 ------- 128 ```py 129 value = Some(5) 130 print(value.unwrap()) 131 # 5 132 133 value = Some(None) 134 print(value.unwrap()) 135 # RuntimeError 136 ``` 137 138 Raises 139 ------ 140 `RuntimeError` 141 If the inner value is `None`. 142 """ 143 if self._value is None: 144 raise RuntimeError("Called `Option.unwrap()` on `None`.") from None 145 146 return self._value 147 148 def unwrap_or(self, default: T, /) -> T: 149 """Unwrap the inner value either returning if its not `None` or returning `default`. 150 151 Example 152 ------- 153 ```py 154 value = Some(5) 155 print(value.unwrap_or(10)) 156 # 5 157 158 # Type hint is required here. 159 value: Option[int] = Some(None) 160 print(value.unwrap_or(10)) 161 # 10 162 ``` 163 """ 164 if self._value is None: 165 return default 166 167 return self._value 168 169 def unwrap_or_else(self, f: FnOnce[T], /) -> T: 170 """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value. 171 172 Example 173 ------- 174 ```py 175 value = Some(5) 176 print(value.unwrap_or_else(lambda: 10)) 177 # 5 178 179 value: Option[bool] = Some(None) 180 print(value.unwrap_or_else(lambda: True)) 181 # True 182 ``` 183 """ 184 if self._value is None: 185 return f() 186 187 return self._value 188 189 @macros.unsafe 190 def unwrap_unchecked(self) -> T: 191 """Returns the contained Some value without checking that the value is not None. 192 193 Example 194 ------- 195 ```py 196 v: Option[float] = Some(1.2) 197 v.unwrap_unchecked() # 1.2 198 199 v: Option[float] = Some(None) 200 print(v.unwrap_unchecked()) # Undefined Behavior 201 ``` 202 """ 203 #! SAFETY: The caller guarantees that the value is not None. 204 return self._value # pyright: ignore 205 206 def expect(self, message: str, /) -> T: 207 """Returns the contained value if it is not `None` otherwise raises a `RuntimeError`. 208 209 Example 210 ------- 211 ```py 212 value = Some("Hello") 213 214 print(value.expect("Value is None")) 215 # "Hello" 216 217 value: Option[str] = Some(None) 218 print(value.expect("Value is None")) 219 # RuntimeError("Value is None") 220 ``` 221 """ 222 if self._value is None: 223 raise RuntimeError(message) 224 225 return self._value 226 227 # *- object transformation -* 228 229 def map(self, f: Fn[T, U], /) -> Option[U]: 230 """Map the inner value to another type. Returning `Some(None)` if `T` is `None`. 231 232 Example 233 ------- 234 ```py 235 value = Some(5.0) 236 237 print(value.map(lambda x: x * 2.0)) 238 # Some(10.0) 239 240 value: Option[bool] = Some(None) 241 print(value) 242 # Some(None) 243 ``` 244 """ 245 if self._value is None: 246 return NOTHING # pyright: ignore 247 248 return Some(f(self._value)) 249 250 def map_or(self, default: U, f: Fn[T, U], /) -> U: 251 """Map the inner value to another type or return `default` if its `None`. 252 253 Example 254 ------- 255 ```py 256 value: Option[float] = Some(5.0) 257 258 # map to int. 259 print(value.map_or(0, int)) 260 # 6 261 262 value: Option[float] = Some(None) 263 print(value.map_or(0, int) 264 # 0 265 ``` 266 """ 267 if self._value is None: 268 return default 269 270 return f(self._value) 271 272 def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U: 273 """Map the inner value to another type, or return `default()` if its `None`. 274 275 Example 276 ------- 277 ```py 278 def default() -> int: 279 return sys.getsizeof(object()) 280 281 value: Option[float] = Some(5.0) 282 283 # map to int. 284 print(value.map_or_else(default, int)) 285 # 6 286 287 value: Option[float] = Some(None) 288 print(value.map_or_else(default, int) 289 # 28 <- size of object() 290 ``` 291 """ 292 if self._value is None: 293 return default() 294 295 return f(self._value) 296 297 def filter(self, predicate: Fn[T, bool]) -> Option[T]: 298 """Returns `Some(None)` if the contained value is `None`, 299 300 otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`. 301 302 Example 303 ------- 304 ```py 305 value = Some([1, 2, 3]) 306 307 print(value.filter(lambda x: 1 in x)) 308 # Some([1, 2, 3]) 309 310 value: Option[int] = Some([1, 2, 3]) # or Some(None) 311 print(value.filter(lambda x: 1 not in x)) 312 # None 313 ``` 314 """ 315 if (value := self._value) is not None: 316 if predicate(value): 317 return Some(value) 318 319 return NOTHING # pyright: ignore 320 321 def ok_or(self, err: U) -> _result.Result[T, U]: 322 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. 323 324 Example 325 ------- 326 ```py 327 xyz: Option[str] = Some("foo") 328 assert xyz.ok_or(None) == Ok("foo") 329 330 xyz: Option[str] = Some(None) 331 assert xyz.ok_or(None) == Err(None) 332 ``` 333 """ 334 if self._value is None: 335 return _result.Err(err) 336 337 return _result.Ok(self._value) 338 339 def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]: 340 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. 341 342 Example 343 ------- 344 ```py 345 xyz: Option[str] = Some("foo") 346 assert xyz.ok_or(None) == Ok("foo") 347 348 xyz: Option[str] = Some(None) 349 assert xyz.ok_or(None) == Err(None) 350 ``` 351 """ 352 if self._value is None: 353 return _result.Err(err()) 354 355 return _result.Ok(self._value) 356 357 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 358 """Zips `self` with `other`. 359 360 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`. 361 362 Example 363 ------- 364 ```py 365 x = Some(1) 366 y = Some("hi") 367 z: Option[str] = Some(None) 368 369 assert x.zip(y) == Some((1, "hi")) 370 assert x.zip(z) == Some(None) 371 ``` 372 """ 373 if self._value is not None and other._value is not None: 374 return Some((self._value, other._value)) 375 376 return NOTHING # type: ignore 377 378 def zip_with( 379 self, other: Option[U], f: collections.Callable[[T, U], T_co] 380 ) -> Option[T_co]: 381 """Zips `self` with `other` using function `f`. 382 383 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`. 384 385 Example 386 ------- 387 ```py 388 @dataclass 389 class Point: 390 x: float 391 y: float 392 393 x, y = Some(32.1), Some(42.4) 394 assert x.zip_with(y, Point) == Some(Point(32.1, 42.4)) 395 ``` 396 """ 397 if self._value is not None and other._value is not None: 398 return Some(f(self._value, other._value)) 399 400 return NOTHING # type: ignore 401 402 # *- Inner operations *- 403 404 def take(self) -> Option[T]: 405 """Take the value from `Self`, Setting it to `None`. 406 407 Example 408 ------- 409 ```py 410 original = Some("Hi") 411 new = original.take() 412 413 print(original, new) 414 # None, Some("Hi") 415 ``` 416 """ 417 if self._value is None: 418 return NOTHING # pyright: ignore 419 420 val = self._value 421 self._value = None 422 return Some(val) 423 424 def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]: 425 """Take the value from `Self`, Setting it to `None` only if predicate returns `True`. 426 427 Example 428 ------- 429 ```py 430 def validate(email: str) -> bool: 431 # you can obviously validate this better. 432 return email.find('@') == 1 433 434 original = Some("flex@gg.com") 435 valid = original.take_if(validate) 436 assert is_allowed.is_some() and original.is_none() 437 438 original = Some("mail.example.com") 439 invalid = original.take_if(validate) 440 assert invalid.is_none() and original.is_some() 441 ``` 442 """ 443 if self.map_or(False, predicate): 444 return self.take() 445 446 return NOTHING # pyright: ignore 447 448 def replace(self, value: T) -> Option[T]: 449 """Replace the contained value with another value. 450 451 Use `Option.insert` if you want to return the original value 452 that got inserted instead of `self` 453 454 Example 455 ------- 456 ```py 457 value: Option[str] = Some(None) 458 value.replace("Hello") 459 # Some("Hello") 460 ``` 461 """ 462 self._value = value 463 return self 464 465 def insert(self, value: T) -> T: 466 """Insert a value into the option, and then return a reference to it. 467 468 This will overwrite the old value if it was already contained. 469 470 Example 471 ------- 472 ```py 473 flag: Option[bool] = Some(None) 474 flag_ref = flag.insert(True) 475 assert flag_ref == True 476 assert flag.unwrap() == True 477 ``` 478 """ 479 self._value = value 480 return value 481 482 def get_or_insert(self, value: T) -> T: 483 """Insert a value into the option if it was `None`, 484 and then return a reference to it. 485 486 Example 487 ------- 488 ```py 489 state: Option[bool] = Some(None) 490 assert state.get_or_insert(True) is True 491 assert state.get_or_insert(False) is True 492 ``` 493 """ 494 if self._value is not None: 495 return self._value 496 497 self._value = value 498 return value 499 500 def get_or_insert_with(self, f: FnOnce[T]) -> T: 501 """Insert a value into the option computed from `f()` if it was `None`, 502 and then return a reference to it. 503 504 Example 505 ------- 506 ```py 507 flag: Option[bool] = Some(None) 508 flag_ref = flag.insert(True) 509 assert flag_ref == True 510 assert flag.unwrap() == True 511 ``` 512 """ 513 if self._value is not None: 514 return self._value 515 516 v = self._value = f() 517 return v 518 519 def and_ok(self, optb: Option[T]) -> Option[T]: 520 """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`. 521 522 aliases: `Option::and` 523 524 Example 525 ------- 526 ```py 527 x = Some(1) 528 y: Option[str] = Some(None) 529 assert x.and_ok(y) == Some(None) 530 531 x: Option[str] = Some(None) 532 y = Some(1) 533 assert x.and_ok(y) == Some(None) 534 535 y: Option[str] = Some("hi") 536 y = Some(100) 537 assert x.and_ok(y) == Some(100) 538 ``` 539 """ 540 if self._value is None or optb._value is None: 541 return optb 542 543 return NOTHING # pyright: ignore 544 545 def and_then(self, f: Fn[T, Option[T]]) -> Option[T]: 546 """Returns `Some(None)` if the contained value is `None`, otherwise call `f()` 547 on `T` and return `Option[T]`. 548 549 Example 550 ------- 551 ```py 552 value = Some(5) 553 print(value.and_then(lambda x: Some(x * 2))) 554 # Some(10) 555 556 value: Option[int] = Some(None) 557 print(value.and_then(lambda x: Some(x * 2))) 558 # Some(None) 559 ``` 560 """ 561 if self._value is None: 562 return NOTHING # pyright: ignore 563 564 return f(self._value) 565 566 def inspect(self, f: Fn[T, typing.Any]) -> Option[T]: 567 """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing. 568 569 Example 570 ------- 571 ```py 572 def debug(x: str) -> None: 573 print("Debugging:", x) 574 575 value = Some("foo") 576 inner = value.inspect(debug).expect("no value to debug") 577 # prints: Debugging: "foo" 578 579 value: Option[str] = Some(None) 580 value.inspect(debug) # prints nothing 581 """ 582 if self._value is not None: 583 f(self._value) 584 585 return self 586 587 # *- Builder methods *- 588 589 def iter(self) -> _iter.Iterator[T]: 590 """Returns an iterator over the contained value. 591 592 Example 593 ------- 594 ```py 595 from sain import Some 596 value = Some("gg").iter() 597 assert value.next() == Some("gg") 598 599 value: Option[int] = Some(None) 600 assert value.iter().next().is_none() 601 ``` 602 """ 603 if self._value is None: 604 return _iter.empty() 605 606 return _iter.once(self._value) 607 608 # *- Boolean checks *- 609 610 def is_some(self) -> bool: 611 """Returns `True` if the contained value is not `None`, otherwise returns `False`. 612 613 Example 614 ------- 615 ```py 616 value = Some(5) 617 print(value.is_some()) 618 # True 619 620 value: Option[int] = Some(None) 621 print(value.is_some()) 622 # False 623 ``` 624 """ 625 return self._value is not None 626 627 def is_some_and(self, predicate: Fn[T, bool]) -> bool: 628 """Returns `True` if the contained value is not `None` and 629 the predicate returns `True`, otherwise returns `False`. 630 631 Example 632 ------- 633 ```py 634 value = Some(5) 635 print(value.is_some_and(lambda x: x > 3)) 636 # True 637 638 value: Option[int] = Some(None) 639 print(value.is_some_and(lambda x: x > 3)) 640 # False 641 ``` 642 """ 643 return self._value is not None and predicate(self._value) 644 645 def is_none(self) -> bool: 646 """Returns `True` if the contained value is `None`, otherwise returns `False`. 647 648 Example 649 ------- 650 ```py 651 value = Some(5) 652 print(value.is_none()) 653 # False 654 655 value: Option[int] = Some(None) 656 print(value.is_none()) 657 # True 658 ``` 659 """ 660 return self._value is None 661 662 def is_none_or(self, f: Fn[T, bool]) -> bool: 663 """Returns `True` if the contained value is `None` or the predicate returns `True`, 664 otherwise returns `False`. 665 666 Example 667 ------- 668 ```py 669 value = Some(5) 670 print(value.is_none_or(lambda x: x > 3)) 671 # False 672 673 value: Option[int] = Some(None) 674 print(value.is_none_or(lambda x: x > 3)) 675 # True 676 ``` 677 """ 678 match self._value: 679 case None: 680 return True 681 case x: 682 return f(x) 683 684 def __repr__(self) -> str: 685 if self._value is None: 686 return "None" 687 return f"Some({self._value!r})" 688 689 __str__ = __repr__ 690 691 def __invert__(self) -> T: 692 return self.unwrap() 693 694 def __or__(self, other: T) -> T: 695 return self.unwrap_or(other) 696 697 def __bool__(self) -> bool: 698 return self.is_some() 699 700 def __eq__(self, other: object) -> bool: 701 if not isinstance(other, Some): 702 return NotImplemented 703 704 return self._value == other._value # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType] 705 706 def __ne__(self, other: object) -> bool: 707 return not self.__eq__(other) 708 709 def __hash__(self) -> int: 710 return hash(self._value)
The Option
type. An object that might be T
or None
.
It is a replacement for typing.Optional[T]
, with more convenient methods to handle the contained value.
Example
value = Some("Hello")
print(value)
# Some("Hello")
# This will unwrap the contained value as long as
# it is not `None` otherwise this will raise an error.
print(value.unwrap())
# "Hello"
none_value = Some(None)
while none_value.unwrap():
# Never unreachable!
# Solving it with `unwrap_or` method to unwrap the value or return a default value.
print(none_value.unwrap_or(10))
# 10
Implementations
This class implements Option in Rust.
105 def transpose(self) -> T | None: 106 """Convert `Option[T]` into `T | None`. 107 108 Examples 109 -------- 110 ```py 111 opt = Some('char') 112 x = opt.transpose() 113 assert x == 'char' 114 115 opt = Some(None) 116 assert opt.transpose() is None 117 ``` 118 """ 119 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
121 def unwrap(self) -> T: 122 """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`. 123 124 It's usually not recommended to use this method in production code, since it raises. 125 126 Example 127 ------- 128 ```py 129 value = Some(5) 130 print(value.unwrap()) 131 # 5 132 133 value = Some(None) 134 print(value.unwrap()) 135 # RuntimeError 136 ``` 137 138 Raises 139 ------ 140 `RuntimeError` 141 If the inner value is `None`. 142 """ 143 if self._value is None: 144 raise RuntimeError("Called `Option.unwrap()` on `None`.") from None 145 146 return self._value
Unwrap the inner value either returning if its not None
or raising a RuntimeError
.
It's usually not recommended to use this method in production code, since it raises.
Example
value = Some(5)
print(value.unwrap())
# 5
value = Some(None)
print(value.unwrap())
# RuntimeError
Raises
RuntimeError
: If the inner value isNone
.
148 def unwrap_or(self, default: T, /) -> T: 149 """Unwrap the inner value either returning if its not `None` or returning `default`. 150 151 Example 152 ------- 153 ```py 154 value = Some(5) 155 print(value.unwrap_or(10)) 156 # 5 157 158 # Type hint is required here. 159 value: Option[int] = Some(None) 160 print(value.unwrap_or(10)) 161 # 10 162 ``` 163 """ 164 if self._value is None: 165 return default 166 167 return self._value
Unwrap the inner value either returning if its not None
or returning default
.
Example
value = Some(5)
print(value.unwrap_or(10))
# 5
# Type hint is required here.
value: Option[int] = Some(None)
print(value.unwrap_or(10))
# 10
169 def unwrap_or_else(self, f: FnOnce[T], /) -> T: 170 """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value. 171 172 Example 173 ------- 174 ```py 175 value = Some(5) 176 print(value.unwrap_or_else(lambda: 10)) 177 # 5 178 179 value: Option[bool] = Some(None) 180 print(value.unwrap_or_else(lambda: True)) 181 # True 182 ``` 183 """ 184 if self._value is None: 185 return f() 186 187 return self._value
Unwrap the inner value either returning if its not None
or calling f
to get a default value.
Example
value = Some(5)
print(value.unwrap_or_else(lambda: 10))
# 5
value: Option[bool] = Some(None)
print(value.unwrap_or_else(lambda: True))
# True
189 @macros.unsafe 190 def unwrap_unchecked(self) -> T: 191 """Returns the contained Some value without checking that the value is not None. 192 193 Example 194 ------- 195 ```py 196 v: Option[float] = Some(1.2) 197 v.unwrap_unchecked() # 1.2 198 199 v: Option[float] = Some(None) 200 print(v.unwrap_unchecked()) # Undefined Behavior 201 ``` 202 """ 203 #! SAFETY: The caller guarantees that the value is not None. 204 return self._value # 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 on None
is considered undefined behavior.
206 def expect(self, message: str, /) -> T: 207 """Returns the contained value if it is not `None` otherwise raises a `RuntimeError`. 208 209 Example 210 ------- 211 ```py 212 value = Some("Hello") 213 214 print(value.expect("Value is None")) 215 # "Hello" 216 217 value: Option[str] = Some(None) 218 print(value.expect("Value is None")) 219 # RuntimeError("Value is None") 220 ``` 221 """ 222 if self._value is None: 223 raise RuntimeError(message) 224 225 return self._value
Returns the contained value if it is not None
otherwise raises a RuntimeError
.
Example
value = Some("Hello")
print(value.expect("Value is None"))
# "Hello"
value: Option[str] = Some(None)
print(value.expect("Value is None"))
# RuntimeError("Value is None")
229 def map(self, f: Fn[T, U], /) -> Option[U]: 230 """Map the inner value to another type. Returning `Some(None)` if `T` is `None`. 231 232 Example 233 ------- 234 ```py 235 value = Some(5.0) 236 237 print(value.map(lambda x: x * 2.0)) 238 # Some(10.0) 239 240 value: Option[bool] = Some(None) 241 print(value) 242 # Some(None) 243 ``` 244 """ 245 if self._value is None: 246 return NOTHING # pyright: ignore 247 248 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)
250 def map_or(self, default: U, f: Fn[T, U], /) -> U: 251 """Map the inner value to another type or return `default` if its `None`. 252 253 Example 254 ------- 255 ```py 256 value: Option[float] = Some(5.0) 257 258 # map to int. 259 print(value.map_or(0, int)) 260 # 6 261 262 value: Option[float] = Some(None) 263 print(value.map_or(0, int) 264 # 0 265 ``` 266 """ 267 if self._value is None: 268 return default 269 270 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
272 def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U: 273 """Map the inner value to another type, or return `default()` if its `None`. 274 275 Example 276 ------- 277 ```py 278 def default() -> int: 279 return sys.getsizeof(object()) 280 281 value: Option[float] = Some(5.0) 282 283 # map to int. 284 print(value.map_or_else(default, int)) 285 # 6 286 287 value: Option[float] = Some(None) 288 print(value.map_or_else(default, int) 289 # 28 <- size of object() 290 ``` 291 """ 292 if self._value is None: 293 return default() 294 295 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()
297 def filter(self, predicate: Fn[T, bool]) -> Option[T]: 298 """Returns `Some(None)` if the contained value is `None`, 299 300 otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`. 301 302 Example 303 ------- 304 ```py 305 value = Some([1, 2, 3]) 306 307 print(value.filter(lambda x: 1 in x)) 308 # Some([1, 2, 3]) 309 310 value: Option[int] = Some([1, 2, 3]) # or Some(None) 311 print(value.filter(lambda x: 1 not in x)) 312 # None 313 ``` 314 """ 315 if (value := self._value) is not None: 316 if predicate(value): 317 return Some(value) 318 319 return NOTHING # pyright: ignore
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
321 def ok_or(self, err: U) -> _result.Result[T, U]: 322 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. 323 324 Example 325 ------- 326 ```py 327 xyz: Option[str] = Some("foo") 328 assert xyz.ok_or(None) == Ok("foo") 329 330 xyz: Option[str] = Some(None) 331 assert xyz.ok_or(None) == Err(None) 332 ``` 333 """ 334 if self._value is None: 335 return _result.Err(err) 336 337 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)
339 def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]: 340 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. 341 342 Example 343 ------- 344 ```py 345 xyz: Option[str] = Some("foo") 346 assert xyz.ok_or(None) == Ok("foo") 347 348 xyz: Option[str] = Some(None) 349 assert xyz.ok_or(None) == Err(None) 350 ``` 351 """ 352 if self._value is None: 353 return _result.Err(err()) 354 355 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)
357 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 358 """Zips `self` with `other`. 359 360 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`. 361 362 Example 363 ------- 364 ```py 365 x = Some(1) 366 y = Some("hi") 367 z: Option[str] = Some(None) 368 369 assert x.zip(y) == Some((1, "hi")) 370 assert x.zip(z) == Some(None) 371 ``` 372 """ 373 if self._value is not None and other._value is not None: 374 return Some((self._value, other._value)) 375 376 return NOTHING # type: ignore
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)
378 def zip_with( 379 self, other: Option[U], f: collections.Callable[[T, U], T_co] 380 ) -> Option[T_co]: 381 """Zips `self` with `other` using function `f`. 382 383 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`. 384 385 Example 386 ------- 387 ```py 388 @dataclass 389 class Point: 390 x: float 391 y: float 392 393 x, y = Some(32.1), Some(42.4) 394 assert x.zip_with(y, Point) == Some(Point(32.1, 42.4)) 395 ``` 396 """ 397 if self._value is not None and other._value is not None: 398 return Some(f(self._value, other._value)) 399 400 return NOTHING # type: ignore
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))
404 def take(self) -> Option[T]: 405 """Take the value from `Self`, Setting it to `None`. 406 407 Example 408 ------- 409 ```py 410 original = Some("Hi") 411 new = original.take() 412 413 print(original, new) 414 # None, Some("Hi") 415 ``` 416 """ 417 if self._value is None: 418 return NOTHING # pyright: ignore 419 420 val = self._value 421 self._value = None 422 return Some(val)
Take the value from Self
, Setting it to None
.
Example
original = Some("Hi")
new = original.take()
print(original, new)
# None, Some("Hi")
424 def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]: 425 """Take the value from `Self`, Setting it to `None` only if predicate returns `True`. 426 427 Example 428 ------- 429 ```py 430 def validate(email: str) -> bool: 431 # you can obviously validate this better. 432 return email.find('@') == 1 433 434 original = Some("flex@gg.com") 435 valid = original.take_if(validate) 436 assert is_allowed.is_some() and original.is_none() 437 438 original = Some("mail.example.com") 439 invalid = original.take_if(validate) 440 assert invalid.is_none() and original.is_some() 441 ``` 442 """ 443 if self.map_or(False, predicate): 444 return self.take() 445 446 return NOTHING # pyright: ignore
Take the value from Self
, Setting it to None
only if predicate returns True
.
Example
def validate(email: str) -> bool:
# you can obviously validate this better.
return email.find('@') == 1
original = Some("flex@gg.com")
valid = original.take_if(validate)
assert is_allowed.is_some() and original.is_none()
original = Some("mail.example.com")
invalid = original.take_if(validate)
assert invalid.is_none() and original.is_some()
448 def replace(self, value: T) -> Option[T]: 449 """Replace the contained value with another value. 450 451 Use `Option.insert` if you want to return the original value 452 that got inserted instead of `self` 453 454 Example 455 ------- 456 ```py 457 value: Option[str] = Some(None) 458 value.replace("Hello") 459 # Some("Hello") 460 ``` 461 """ 462 self._value = value 463 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")
465 def insert(self, value: T) -> T: 466 """Insert a value into the option, and then return a reference to it. 467 468 This will overwrite the old value if it was already contained. 469 470 Example 471 ------- 472 ```py 473 flag: Option[bool] = Some(None) 474 flag_ref = flag.insert(True) 475 assert flag_ref == True 476 assert flag.unwrap() == True 477 ``` 478 """ 479 self._value = value 480 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
482 def get_or_insert(self, value: T) -> T: 483 """Insert a value into the option if it was `None`, 484 and then return a reference to it. 485 486 Example 487 ------- 488 ```py 489 state: Option[bool] = Some(None) 490 assert state.get_or_insert(True) is True 491 assert state.get_or_insert(False) is True 492 ``` 493 """ 494 if self._value is not None: 495 return self._value 496 497 self._value = value 498 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
500 def get_or_insert_with(self, f: FnOnce[T]) -> T: 501 """Insert a value into the option computed from `f()` if it was `None`, 502 and then return a reference to it. 503 504 Example 505 ------- 506 ```py 507 flag: Option[bool] = Some(None) 508 flag_ref = flag.insert(True) 509 assert flag_ref == True 510 assert flag.unwrap() == True 511 ``` 512 """ 513 if self._value is not None: 514 return self._value 515 516 v = self._value = f() 517 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
519 def and_ok(self, optb: Option[T]) -> Option[T]: 520 """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`. 521 522 aliases: `Option::and` 523 524 Example 525 ------- 526 ```py 527 x = Some(1) 528 y: Option[str] = Some(None) 529 assert x.and_ok(y) == Some(None) 530 531 x: Option[str] = Some(None) 532 y = Some(1) 533 assert x.and_ok(y) == Some(None) 534 535 y: Option[str] = Some("hi") 536 y = Some(100) 537 assert x.and_ok(y) == Some(100) 538 ``` 539 """ 540 if self._value is None or optb._value is None: 541 return optb 542 543 return NOTHING # pyright: ignore
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)
y: Option[str] = Some("hi")
y = Some(100)
assert x.and_ok(y) == Some(100)
545 def and_then(self, f: Fn[T, Option[T]]) -> Option[T]: 546 """Returns `Some(None)` if the contained value is `None`, otherwise call `f()` 547 on `T` and return `Option[T]`. 548 549 Example 550 ------- 551 ```py 552 value = Some(5) 553 print(value.and_then(lambda x: Some(x * 2))) 554 # Some(10) 555 556 value: Option[int] = Some(None) 557 print(value.and_then(lambda x: Some(x * 2))) 558 # Some(None) 559 ``` 560 """ 561 if self._value is None: 562 return NOTHING # pyright: ignore 563 564 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)
566 def inspect(self, f: Fn[T, typing.Any]) -> Option[T]: 567 """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing. 568 569 Example 570 ------- 571 ```py 572 def debug(x: str) -> None: 573 print("Debugging:", x) 574 575 value = Some("foo") 576 inner = value.inspect(debug).expect("no value to debug") 577 # prints: Debugging: "foo" 578 579 value: Option[str] = Some(None) 580 value.inspect(debug) # prints nothing 581 """ 582 if self._value is not None: 583 f(self._value) 584 585 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
589 def iter(self) -> _iter.Iterator[T]: 590 """Returns an iterator over the contained value. 591 592 Example 593 ------- 594 ```py 595 from sain import Some 596 value = Some("gg").iter() 597 assert value.next() == Some("gg") 598 599 value: Option[int] = Some(None) 600 assert value.iter().next().is_none() 601 ``` 602 """ 603 if self._value is None: 604 return _iter.empty() 605 606 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()
610 def is_some(self) -> bool: 611 """Returns `True` if the contained value is not `None`, otherwise returns `False`. 612 613 Example 614 ------- 615 ```py 616 value = Some(5) 617 print(value.is_some()) 618 # True 619 620 value: Option[int] = Some(None) 621 print(value.is_some()) 622 # False 623 ``` 624 """ 625 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
627 def is_some_and(self, predicate: Fn[T, bool]) -> bool: 628 """Returns `True` if the contained value is not `None` and 629 the predicate returns `True`, otherwise returns `False`. 630 631 Example 632 ------- 633 ```py 634 value = Some(5) 635 print(value.is_some_and(lambda x: x > 3)) 636 # True 637 638 value: Option[int] = Some(None) 639 print(value.is_some_and(lambda x: x > 3)) 640 # False 641 ``` 642 """ 643 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
645 def is_none(self) -> bool: 646 """Returns `True` if the contained value is `None`, otherwise returns `False`. 647 648 Example 649 ------- 650 ```py 651 value = Some(5) 652 print(value.is_none()) 653 # False 654 655 value: Option[int] = Some(None) 656 print(value.is_none()) 657 # True 658 ``` 659 """ 660 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
662 def is_none_or(self, f: Fn[T, bool]) -> bool: 663 """Returns `True` if the contained value is `None` or the predicate returns `True`, 664 otherwise returns `False`. 665 666 Example 667 ------- 668 ```py 669 value = Some(5) 670 print(value.is_none_or(lambda x: x > 3)) 671 # False 672 673 value: Option[int] = Some(None) 674 print(value.is_none_or(lambda x: x > 3)) 675 # True 676 ``` 677 """ 678 match self._value: 679 case None: 680 return True 681 case x: 682 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
937@rustc_diagnostic_item("Iter") 938@typing.final 939@diagnostic 940class Iter(typing.Generic[Item], Iterator[Item]): 941 """a lazy iterator that has its items ready in-memory. 942 943 This is similar to Rust `std::slice::Iter<T>` item which iterables can build 944 from this via `.iter()` method. 945 946 Example 947 ------- 948 ```py 949 iterator = Iter([1, 2, 3]) 950 951 # Limit the results to 2. 952 for item in iterator.take(2): 953 print(item) 954 # 1 955 # 2 956 957 # Filter the results. 958 for item in iterator.filter(lambda item: item > 1): 959 print(item) 960 # 2 961 # 3 962 # 3 963 964 # Indexing is supported. 965 print(iterator[0]) 966 # 1 967 ``` 968 969 Parameters 970 ---------- 971 items: `Iterable[Item]` 972 The items to iterate over. This can be anything that implements `__iter__` and `__next__`. 973 """ 974 975 __slots__ = ("_it",) 976 977 def __init__(self, iterable: collections.Iterable[Item]) -> None: 978 self._it = iter(iterable) 979 980 def clone(self) -> Iter[Item]: 981 """Return a copy of this iterator. 982 983 ```py 984 it = Iterator([1, 2, 3]) 985 986 for i in it.clone(): 987 ... 988 989 # The actual iterator hasn't been exhausted. 990 assert it.count() == 3 991 ``` 992 """ 993 return Iter(copy.copy(self._it)) 994 995 def __next__(self) -> Item: 996 return next(self._it) 997 998 def __getitem__(self, index: int) -> Option[Item]: 999 try: 1000 return self.skip(index).first() 1001 except IndexError: 1002 unreachable() 1003 1004 def __contains__(self, item: Item) -> bool: 1005 return item in self._it
a lazy iterator that has its items ready in-memory.
This is similar to Rust std::slice::Iter<T>
item which iterables can build
from this via sain.iter
method.
Example
iterator = Iter([1, 2, 3])
# Limit the results to 2.
for item in iterator.take(2):
print(item)
# 1
# 2
# Filter the results.
for item in iterator.filter(lambda item: item > 1):
print(item)
# 2
# 3
# 3
# Indexing is supported.
print(iterator[0])
# 1
Parameters
- items (
Iterable[Item]
): The items to iterate over. This can be anything that implements__iter__
and__next__
. - # Implementations
- **This class implements Iter:
980 def clone(self) -> Iter[Item]: 981 """Return a copy of this iterator. 982 983 ```py 984 it = Iterator([1, 2, 3]) 985 986 for i in it.clone(): 987 ... 988 989 # The actual iterator hasn't been exhausted. 990 assert it.count() == 3 991 ``` 992 """ 993 return Iter(copy.copy(self._it))
Return a copy of this iterator.
it = Iterator([1, 2, 3])
for i in it.clone():
...
# The actual iterator hasn't been exhausted.
assert it.count() == 3
120class Iterator( 121 typing.Generic[Item], 122 abc.ABC, 123 _default.Default["Empty[Item]"], 124): 125 """An abstract interface for dealing with iterators. 126 127 This is exactly the same trait as `core::iter::Iterator` trait from Rust. 128 129 This is the main interface that any type can implement by basically inheriting from it. 130 The method `__next__` is the only method that needs to be implemented, You get all the other methods for free. 131 132 If you want to use a ready iterator for general purposes, Use `Iter`. This interface is only for implementors 133 and type hints. 134 135 Example 136 ------- 137 ```py 138 @dataclass 139 class Counter(Iterator[int]): 140 start: int = 0 141 stop: int | None = None 142 143 # implement the required method. 144 def __next__(self) -> int: 145 result = self.start 146 self.start += 1 147 148 if self.stop is not None and result >= self.stop: 149 raise StopIteration 150 151 return result 152 153 counter = Counter(start=0, stop=10) 154 for i in counter.map(lambda x: x * 2): # multiply each number 155 ... 156 ``` 157 """ 158 159 @abc.abstractmethod 160 def __next__(self) -> Item: 161 raise NotImplementedError 162 163 ################### 164 # const functions # 165 ################### 166 167 @staticmethod 168 @typing.final 169 def default() -> Empty[Item]: 170 """Return the default iterator for this type. It returns an empty iterator. 171 172 Example 173 ------- 174 ```py 175 it: Iterator[int] = Iter.default() 176 assert t.next().is_none() 177 ``` 178 """ 179 return Empty() 180 181 @typing.overload 182 def collect(self) -> collections.Sequence[Item]: ... 183 184 @typing.overload 185 def collect( 186 self, *, cast: collections.Callable[[Item], OtherItem] 187 ) -> collections.Sequence[OtherItem]: ... 188 189 @typing.final 190 def collect( 191 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 192 ) -> collections.Sequence[Item] | collections.Sequence[OtherItem]: 193 """Collects all items in the iterator into an immutable sequence. 194 195 Example 196 ------- 197 ```py 198 iterator = Iter(range(3)) 199 iterator.collect() 200 # (0, 1, 2, 3) 201 iterator.collect(cast=str) # Map each element and collect it. 202 # ('0', '1', '2', '3') 203 ``` 204 205 Parameters 206 ---------- 207 cast: `T | None` 208 An optional type to cast the items into. 209 If not provided the items will be returned as it's original type. 210 """ 211 if cast is not None: 212 return tuple(cast(i) for i in self) 213 214 return tuple(_ for _ in self) 215 216 @typing.final 217 def collect_into(self, collection: Collector[Item]) -> None: 218 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 219 220 Example 221 ------- 222 ```py 223 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 224 uniques = set() 225 iterator.collect_into(uniques) 226 # assert uniques == {1, 2, 3, 4, 6} 227 ``` 228 229 Parameters 230 ---------- 231 collection: `MutableSequence[T]` | `set[T]` 232 The collection to extend the items in this iterator with. 233 """ 234 if isinstance(collection, collections.MutableSequence): 235 collection.extend(_ for _ in self) 236 elif isinstance(collection, collections.MutableSet): 237 collection.update(_ for _ in self) 238 else: 239 for idx, item in enumerate(self): 240 collection[idx] = item 241 242 @typing.final 243 def to_vec(self) -> _vec.Vec[Item]: 244 """Convert this iterator into `Vec[T]`. 245 246 Example 247 ------- 248 ```py 249 it = sain.iter.once(0) 250 vc = it.to_vec() 251 252 assert to_vec == [0] 253 ``` 254 """ 255 return _vec.Vec(_ for _ in self) 256 257 @typing.final 258 def sink(self) -> None: 259 """Consume all elements from this iterator, flushing it into the sink. 260 261 Example 262 ------- 263 ```py 264 it = Iter((1, 2, 3)) 265 it.sink() 266 assert it.next().is_none() 267 ``` 268 """ 269 for _ in self: 270 pass 271 272 @typing.final 273 def raw_parts(self) -> collections.Generator[Item, None, None]: 274 """Decompose all elements from this iterator, yielding it one by one 275 as a normal generator. 276 277 This mainly used for objects that needs to satisfy its exact type. 278 279 ```py 280 it = Iter("cba") 281 sort = sorted(it.raw_parts()) 282 283 assert it.count() == 0 284 assert sort == ["a", "b", "c"] 285 ``` 286 """ 287 for item in self: 288 yield item 289 290 ################## 291 # default impl's # 292 ################## 293 294 def next(self) -> Option[Item]: 295 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 296 297 Example 298 ------- 299 ```py 300 iterator = Iter(["1", "2"]) 301 assert iterator.next() == Some("1") 302 assert iterator.next() == Some("2") 303 assert iterator.next().is_none() 304 ``` 305 """ 306 try: 307 return _option.Some(self.__next__()) 308 except StopIteration: 309 # ! SAFETY: No more items in the iterator. 310 return _option.NOTHING # pyright: ignore 311 312 def cloned(self) -> Cloned[Item]: 313 """Creates an iterator which shallow copies its elements by reference. 314 315 If you need a copy of the actual iterator and not the elements. 316 use `Iter.clone()` 317 318 .. note:: 319 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 320 on each item that is being yielded. 321 322 Example 323 ------- 324 ```py 325 @dataclass 326 class User: 327 users_ids: list[int] = [] 328 329 # An iterator which elements points to the same user. 330 user = User() 331 it = Iter((user, user)) 332 333 for u in it.cloned(): 334 u.user_ids.append(1) 335 336 # We iterated over the same user pointer twice and appended "1" 337 # since `copy` returns a shallow copy of nested structures. 338 assert len(user.user_ids) == 2 339 ``` 340 """ 341 return Cloned(self) 342 343 def copied(self) -> Copied[Item]: 344 """Creates an iterator which copies all of its elements by value. 345 346 If you only need a copy of the item reference, Use `.cloned()` instead. 347 348 .. note:: 349 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 350 on each item that is being yielded. 351 352 Example 353 ------- 354 ```py 355 @dataclass 356 class User: 357 users_ids: list[int] = [] 358 359 # An iterator which elements points to the same user. 360 user = User() 361 it = Iter((user, user)) 362 363 for u in it.copied(): 364 # A new list is created for each item. 365 u.user_ids.append(1) 366 367 # The actual list is untouched since we consumed a deep copy of it. 368 assert len(user.user_ids) == 0 369 ``` 370 """ 371 return Copied(self) 372 373 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 374 """Maps each item in the iterator to another type. 375 376 Example 377 ------- 378 ```py 379 iterator = Iter(["1", "2", "3"]).map(int) 380 381 for item in iterator: 382 assert isinstance(item, int) 383 ``` 384 385 Parameters 386 ---------- 387 predicate: `Callable[[Item], OtherItem]` 388 The function to map each item in the iterator to the other type. 389 """ 390 return Map(self, fn) 391 392 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 393 """Filters the iterator to only yield items that match the predicate. 394 395 Example 396 ------- 397 ```py 398 places = Iter(['London', 'Paris', 'Los Angeles']) 399 for place in places.filter(lambda place: place.startswith('L')): 400 print(place) 401 402 # London 403 # Los Angeles 404 ``` 405 """ 406 return Filter(self, predicate) 407 408 def take(self, count: int) -> Take[Item]: 409 """Take the first number of items until the number of items 410 are yielded or the end of the iterator is exhausted. 411 412 Example 413 ------- 414 ```py 415 iterator = Iter(['c', 'x', 'y']) 416 417 for x in iterator.take(2): 418 assert x in ('c', 'x') 419 420 # <Iter(['c', 'x'])> 421 ``` 422 """ 423 return Take(self, count) 424 425 def skip(self, count: int) -> Skip[Item]: 426 """Skips the first number of items in the iterator. 427 428 Example 429 ------- 430 ```py 431 iterator = Iter((1, 2, 3, 4)) 432 for i in iterator.skip(2): 433 print(i) 434 435 # 3 436 # 4 437 ``` 438 """ 439 return Skip(self, count) 440 441 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 442 """Create a new iterator that yields a tuple of the index and item. 443 444 Example 445 ------- 446 ```py 447 iterator = Iter([1, 2, 3]) 448 for index, item in iterator.enumerate(): 449 print(index, item) 450 451 # 0 1 452 # 1 2 453 # 2 3 454 ``` 455 """ 456 return Enumerate(self, start) 457 458 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 459 """yields items from the iterator while predicate returns `True`. 460 461 The rest of the items are discarded as soon as the predicate returns `False` 462 463 Example 464 ------- 465 ```py 466 iterator = Iter(['a', 'ab', 'xd', 'ba']) 467 for x in iterator.take_while(lambda x: 'a' in x): 468 print(x) 469 470 # a 471 # ab 472 ``` 473 474 Parameters 475 ---------- 476 predicate: `collections.Callable[[Item], bool]` 477 The function to predicate each item in the iterator. 478 """ 479 return TakeWhile(self, f) 480 481 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 482 """Yields items from the iterator while predicate returns `False`. 483 484 Example 485 ------- 486 ```py 487 iterator = Iter(['a', 'ab', 'xd', 'ba']) 488 for x in iterator.drop_while(lambda x: 'a' in x): 489 print(x) 490 491 # xd 492 # ba 493 ``` 494 495 Parameters 496 ---------- 497 predicate: `collections.Callable[[Item], bool]` 498 The function to predicate each item in the iterator. 499 """ 500 return DropWhile(self, f) 501 502 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 503 """Returns an iterator over `chunk_size` elements of the iterator at a time, 504 starting at the beginning of the iterator. 505 506 Example 507 ------- 508 ```py 509 iter = Iter(['a', 'b', 'c', 'd', 'e']) 510 chunks = iter.chunks() 511 assert chunks.next().unwrap() == ['a', 'b'] 512 assert chunks.next().unwrap() == ['c', 'd'] 513 assert chunks.next().unwrap() == ['e'] 514 assert chunks.next().is_none() 515 ``` 516 """ 517 return Chunks(self, chunk_size) 518 519 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 520 """Return `True` if all items in the iterator match the predicate. 521 522 Example 523 ------- 524 ```py 525 iterator = Iter([1, 2, 3]) 526 while iterator.all(lambda item: isinstance(item, int)): 527 print("Still all integers") 528 continue 529 # Still all integers 530 ``` 531 532 Parameters 533 ---------- 534 predicate: `collections.Callable[[Item], bool]` 535 The function to test each item in the iterator. 536 """ 537 return all(predicate(item) for item in self) 538 539 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 540 """`True` if any items in the iterator match the predicate. 541 542 Example 543 ------- 544 ```py 545 iterator = Iter([1, 2, 3]) 546 if iterator.any(lambda item: isinstance(item, int)): 547 print("At least one item is an int.") 548 # At least one item is an int. 549 ``` 550 551 Parameters 552 ---------- 553 predicate: `collections.Callable[[Item], bool]` 554 The function to test each item in the iterator. 555 """ 556 return any(predicate(item) for item in self) 557 558 def zip( 559 self, other: collections.Iterable[OtherItem] 560 ) -> Iter[tuple[Item, OtherItem]]: 561 """Zips the iterator with another iterable. 562 563 Example 564 ------- 565 ```py 566 iterator = Iter([1, 2, 3]) 567 for item, other_item in iterator.zip([4, 5, 6]): 568 assert item == other_item 569 <Iter([(1, 4), (2, 5), (3, 6)])> 570 ``` 571 572 Parameters 573 ---------- 574 other: `Iter[OtherItem]` 575 The iterable to zip with. 576 577 Returns 578 ------- 579 `Iter[tuple[Item, OtherItem]]` 580 The zipped iterator. 581 582 """ 583 return Iter(zip(self.raw_parts(), other)) 584 585 def sort( 586 self, 587 *, 588 key: collections.Callable[[Item], _typeshed.SupportsRichComparison], 589 reverse: bool = False, 590 ) -> Iter[Item]: 591 """Sorts the iterator. 592 593 Example 594 ------- 595 ```py 596 iterator = Iter([3, 1, 6, 7]) 597 for item in iterator.sort(key=lambda item: item < 3): 598 print(item) 599 # 1 600 # 3 601 # 6 602 # 7 603 ``` 604 605 Parameters 606 ---------- 607 key: `collections.Callable[[Item], Any]` 608 The function to sort by. 609 reverse: `bool` 610 Whether to reverse the sort. 611 612 """ 613 return Iter(sorted(self.raw_parts(), 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 Example 619 ------- 620 ```py 621 iterator = Iter([3, 1, 6, 7]) 622 for item in iterator.reversed(): 623 print(item) 624 # 7 625 # 6 626 # 1 627 # 3 628 ``` 629 """ 630 # NOTE: In order to reverse the iterator we need to 631 # first collect it into some collection. 632 return Iter(reversed(self.collect())) 633 634 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 635 """Returns a new iterator that yields all items from both iterators. 636 637 Example 638 ------- 639 ```py 640 iterator = Iter([1, 2, 3]) 641 other = [4, 5, 6] 642 643 for item in iterator.union(other): 644 print(item) 645 # 1 646 # 2 647 # 3 648 # 4 649 # 5 650 # 6 651 ``` 652 653 Parameters 654 ---------- 655 other: `Iter[Item]` 656 The iterable to union with. 657 """ 658 return Iter(itertools.chain(self.raw_parts(), other)) 659 660 def first(self) -> Option[Item]: 661 """Returns the first item in the iterator. 662 663 Example 664 ------- 665 ```py 666 iterator = Iter([3, 1, 6, 7]) 667 iterator.first().is_some_and(lambda x: x == 3) 668 ``` 669 """ 670 return self.take(1).next() 671 672 def last(self) -> Option[Item]: 673 """Returns the last item in the iterator. 674 675 Example 676 ------- 677 ```py 678 iterator = Iter([3, 1, 6, 7]) 679 iterator.last().is_some_and(lambda x: x == 7) 680 ``` 681 """ 682 return self.reversed().first() 683 684 def count(self) -> int: 685 """Return the count of elements in memory this iterator has. 686 687 Example 688 ------- 689 ```py 690 it = Iter(range(3)) 691 assert it.count() == 3 692 ``` 693 """ 694 count = 0 695 for _ in self: 696 count += 1 697 698 return count 699 700 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 701 """Searches for an element of an iterator that satisfies a predicate. 702 703 If you want the position of the element, use `Iterator.position` instead. 704 705 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 706 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 707 708 Example 709 ------- 710 ```py 711 it = Iter(range(10)) 712 item = it.find(lambda num: num > 5) 713 print(item) # 6 714 ``` 715 """ 716 for item in self: 717 if predicate(item): 718 return _option.Some(item) 719 720 # no more items 721 return _option.NOTHING # pyright: ignore 722 723 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 724 """Searches for the position of an element in the iterator that satisfies a predicate. 725 726 If you want the object itself, use `Iterator.find` instead. 727 728 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 729 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 730 731 Example 732 ------- 733 ```py 734 it = Iter(range(10)) 735 position = it.find(lambda num: num > 5) 736 assert position.unwrap() == 6 737 ``` 738 """ 739 for position, value in self.enumerate(): 740 if predicate(value): 741 return _option.Some(position) 742 743 # no more items 744 return _option.NOTHING # pyright: ignore 745 746 def fold( 747 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 748 ) -> OtherItem: 749 """Folds every element into an accumulator by applying an operation, returning the final result. 750 751 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 752 The closure returns the value that the accumulator should have for the next iteration. 753 754 The initial value is the value the accumulator will have on the first call. 755 756 After applying this closure to every element of the iterator, fold() returns the accumulator. 757 758 This operation is sometimes called ‘reduce’ or ‘inject’. 759 760 Example 761 ------- 762 ```py 763 a = Iter([1, 2, 3, 4]) 764 sum = a.fold(0, lambda acc, elem: acc + elem) 765 assert sum == 10 766 ``` 767 """ 768 accum = init 769 while True: 770 try: 771 x = self.__next__() 772 accum = f(accum, x) 773 except StopIteration: 774 break 775 776 return accum 777 778 def sum(self: Sum) -> int: 779 """Sums an iterator of a possible type that can be converted to an integer. 780 781 Example 782 ------- 783 ```py 784 numbers: Iterator[str] = Iter(["1", "2", "3"]) 785 total = numbers.sum() 786 assert total == 6 787 ``` 788 """ 789 return sum(int(_) for _ in self) 790 791 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 792 """Calls `func` on each item in the iterator. 793 794 Example 795 ------- 796 ```py 797 iterator = Iter([1, 2, 3]) 798 iterator.for_each(lambda item: print(item)) 799 # 1 800 # 2 801 # 3 802 ``` 803 804 Parameters 805 ---------- 806 func: `collections.Callable[[Item], typing.Any]` 807 The function to call on each item in the iterator. 808 """ 809 for item in self: 810 func(item) 811 812 async def async_for_each( 813 self, 814 func: collections.Callable[ 815 [Item], collections.Coroutine[None, typing.Any, OtherItem] 816 ], 817 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 818 """Calls the async function on each item in the iterator concurrently. 819 820 Example 821 ------- 822 ```py 823 async def create_user(username: str) -> None: 824 async with aiohttp.request("POST", f'.../{username}') as r: 825 return await r.json() 826 827 async def main(): 828 users = sain.into_iter(["danny", "legalia"]) 829 results = await users.async_for_each(lambda username: create_user(username)) 830 for k, v in results.unwrap().items(): 831 ... 832 ``` 833 834 Parameters 835 ---------- 836 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 837 The async function to call on each item in the iterator. 838 """ 839 return await futures.join(*(func(item) for item in self)) 840 841 def __reversed__(self) -> Iter[Item]: 842 return self.reversed() 843 844 def __setitem__(self, _: None, __: None) -> typing.NoReturn: 845 raise NotImplementedError( 846 f"{type(self).__name__} doesn't support item assignment." 847 ) from None 848 849 def __repr__(self) -> str: 850 return "<Iterator>" 851 852 def __copy__(self) -> Cloned[Item]: 853 return self.cloned() 854 855 def __deepcopy__( 856 self, memo: collections.MutableMapping[int, typing.Any], / 857 ) -> Copied[Item]: 858 return self.copied() 859 860 def __len__(self) -> int: 861 return self.count() 862 863 def __iter__(self) -> Iterator[Item]: 864 return self
An abstract interface for dealing with iterators.
This is exactly the same trait as core::iter::Iterator
trait from Rust.
This is the main interface that any type can implement by basically inheriting from it.
The method __next__
is the only method that needs to be implemented, You get all the other methods for free.
If you want to use a ready iterator for general purposes, Use Iter
. This interface is only for implementors
and type hints.
Example
@dataclass
class 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
...
167 @staticmethod 168 @typing.final 169 def default() -> Empty[Item]: 170 """Return the default iterator for this type. It returns an empty iterator. 171 172 Example 173 ------- 174 ```py 175 it: Iterator[int] = Iter.default() 176 assert t.next().is_none() 177 ``` 178 """ 179 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()
189 @typing.final 190 def collect( 191 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 192 ) -> collections.Sequence[Item] | collections.Sequence[OtherItem]: 193 """Collects all items in the iterator into an immutable sequence. 194 195 Example 196 ------- 197 ```py 198 iterator = Iter(range(3)) 199 iterator.collect() 200 # (0, 1, 2, 3) 201 iterator.collect(cast=str) # Map each element and collect it. 202 # ('0', '1', '2', '3') 203 ``` 204 205 Parameters 206 ---------- 207 cast: `T | None` 208 An optional type to cast the items into. 209 If not provided the items will be returned as it's original type. 210 """ 211 if cast is not None: 212 return tuple(cast(i) for i in self) 213 214 return tuple(_ for _ in self)
Collects all items in the iterator into an immutable sequence.
Example
iterator = Iter(range(3))
iterator.collect()
# (0, 1, 2, 3)
iterator.collect(cast=str) # Map each element and collect it.
# ('0', '1', '2', '3')
Parameters
- cast (
T | None
): An optional type to cast the items into. If not provided the items will be returned as it's original type.
216 @typing.final 217 def collect_into(self, collection: Collector[Item]) -> None: 218 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 219 220 Example 221 ------- 222 ```py 223 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 224 uniques = set() 225 iterator.collect_into(uniques) 226 # assert uniques == {1, 2, 3, 4, 6} 227 ``` 228 229 Parameters 230 ---------- 231 collection: `MutableSequence[T]` | `set[T]` 232 The collection to extend the items in this iterator with. 233 """ 234 if isinstance(collection, collections.MutableSequence): 235 collection.extend(_ for _ in self) 236 elif isinstance(collection, collections.MutableSet): 237 collection.update(_ for _ in self) 238 else: 239 for idx, item in enumerate(self): 240 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.
242 @typing.final 243 def to_vec(self) -> _vec.Vec[Item]: 244 """Convert this iterator into `Vec[T]`. 245 246 Example 247 ------- 248 ```py 249 it = sain.iter.once(0) 250 vc = it.to_vec() 251 252 assert to_vec == [0] 253 ``` 254 """ 255 return _vec.Vec(_ for _ in self)
Convert this iterator into Vec[T]
.
Example
it = sain.iter.once(0)
vc = it.to_vec()
assert to_vec == [0]
257 @typing.final 258 def sink(self) -> None: 259 """Consume all elements from this iterator, flushing it into the sink. 260 261 Example 262 ------- 263 ```py 264 it = Iter((1, 2, 3)) 265 it.sink() 266 assert it.next().is_none() 267 ``` 268 """ 269 for _ in self: 270 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()
272 @typing.final 273 def raw_parts(self) -> collections.Generator[Item, None, None]: 274 """Decompose all elements from this iterator, yielding it one by one 275 as a normal generator. 276 277 This mainly used for objects that needs to satisfy its exact type. 278 279 ```py 280 it = Iter("cba") 281 sort = sorted(it.raw_parts()) 282 283 assert it.count() == 0 284 assert sort == ["a", "b", "c"] 285 ``` 286 """ 287 for item in self: 288 yield item
Decompose all elements from this iterator, yielding it one by one as a normal generator.
This mainly used for objects that needs to satisfy its exact type.
it = Iter("cba")
sort = sorted(it.raw_parts())
assert it.count() == 0
assert sort == ["a", "b", "c"]
294 def next(self) -> Option[Item]: 295 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 296 297 Example 298 ------- 299 ```py 300 iterator = Iter(["1", "2"]) 301 assert iterator.next() == Some("1") 302 assert iterator.next() == Some("2") 303 assert iterator.next().is_none() 304 ``` 305 """ 306 try: 307 return _option.Some(self.__next__()) 308 except StopIteration: 309 # ! SAFETY: No more items in the iterator. 310 return _option.NOTHING # pyright: ignore
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()
312 def cloned(self) -> Cloned[Item]: 313 """Creates an iterator which shallow copies its elements by reference. 314 315 If you need a copy of the actual iterator and not the elements. 316 use `Iter.clone()` 317 318 .. note:: 319 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 320 on each item that is being yielded. 321 322 Example 323 ------- 324 ```py 325 @dataclass 326 class User: 327 users_ids: list[int] = [] 328 329 # An iterator which elements points to the same user. 330 user = User() 331 it = Iter((user, user)) 332 333 for u in it.cloned(): 334 u.user_ids.append(1) 335 336 # We iterated over the same user pointer twice and appended "1" 337 # since `copy` returns a shallow copy of nested structures. 338 assert len(user.user_ids) == 2 339 ``` 340 """ 341 return Cloned(self)
Creates an iterator which shallow copies its elements by reference.
If you need a copy of the actual iterator and not the elements.
use Iter.clone()
This method calls copy.copy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.cloned():
u.user_ids.append(1)
# We iterated over the same user pointer twice and appended "1"
# since `copy` returns a shallow copy of nested structures.
assert len(user.user_ids) == 2
343 def copied(self) -> Copied[Item]: 344 """Creates an iterator which copies all of its elements by value. 345 346 If you only need a copy of the item reference, Use `.cloned()` instead. 347 348 .. note:: 349 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 350 on each item that is being yielded. 351 352 Example 353 ------- 354 ```py 355 @dataclass 356 class User: 357 users_ids: list[int] = [] 358 359 # An iterator which elements points to the same user. 360 user = User() 361 it = Iter((user, user)) 362 363 for u in it.copied(): 364 # A new list is created for each item. 365 u.user_ids.append(1) 366 367 # The actual list is untouched since we consumed a deep copy of it. 368 assert len(user.user_ids) == 0 369 ``` 370 """ 371 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
373 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 374 """Maps each item in the iterator to another type. 375 376 Example 377 ------- 378 ```py 379 iterator = Iter(["1", "2", "3"]).map(int) 380 381 for item in iterator: 382 assert isinstance(item, int) 383 ``` 384 385 Parameters 386 ---------- 387 predicate: `Callable[[Item], OtherItem]` 388 The function to map each item in the iterator to the other type. 389 """ 390 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.
392 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 393 """Filters the iterator to only yield items that match the predicate. 394 395 Example 396 ------- 397 ```py 398 places = Iter(['London', 'Paris', 'Los Angeles']) 399 for place in places.filter(lambda place: place.startswith('L')): 400 print(place) 401 402 # London 403 # Los Angeles 404 ``` 405 """ 406 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
408 def take(self, count: int) -> Take[Item]: 409 """Take the first number of items until the number of items 410 are yielded or the end of the iterator is exhausted. 411 412 Example 413 ------- 414 ```py 415 iterator = Iter(['c', 'x', 'y']) 416 417 for x in iterator.take(2): 418 assert x in ('c', 'x') 419 420 # <Iter(['c', 'x'])> 421 ``` 422 """ 423 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'])>
425 def skip(self, count: int) -> Skip[Item]: 426 """Skips the first number of items in the iterator. 427 428 Example 429 ------- 430 ```py 431 iterator = Iter((1, 2, 3, 4)) 432 for i in iterator.skip(2): 433 print(i) 434 435 # 3 436 # 4 437 ``` 438 """ 439 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
441 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 442 """Create a new iterator that yields a tuple of the index and item. 443 444 Example 445 ------- 446 ```py 447 iterator = Iter([1, 2, 3]) 448 for index, item in iterator.enumerate(): 449 print(index, item) 450 451 # 0 1 452 # 1 2 453 # 2 3 454 ``` 455 """ 456 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
458 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 459 """yields items from the iterator while predicate returns `True`. 460 461 The rest of the items are discarded as soon as the predicate returns `False` 462 463 Example 464 ------- 465 ```py 466 iterator = Iter(['a', 'ab', 'xd', 'ba']) 467 for x in iterator.take_while(lambda x: 'a' in x): 468 print(x) 469 470 # a 471 # ab 472 ``` 473 474 Parameters 475 ---------- 476 predicate: `collections.Callable[[Item], bool]` 477 The function to predicate each item in the iterator. 478 """ 479 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.
481 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 482 """Yields items from the iterator while predicate returns `False`. 483 484 Example 485 ------- 486 ```py 487 iterator = Iter(['a', 'ab', 'xd', 'ba']) 488 for x in iterator.drop_while(lambda x: 'a' in x): 489 print(x) 490 491 # xd 492 # ba 493 ``` 494 495 Parameters 496 ---------- 497 predicate: `collections.Callable[[Item], bool]` 498 The function to predicate each item in the iterator. 499 """ 500 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.
502 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 503 """Returns an iterator over `chunk_size` elements of the iterator at a time, 504 starting at the beginning of the iterator. 505 506 Example 507 ------- 508 ```py 509 iter = Iter(['a', 'b', 'c', 'd', 'e']) 510 chunks = iter.chunks() 511 assert chunks.next().unwrap() == ['a', 'b'] 512 assert chunks.next().unwrap() == ['c', 'd'] 513 assert chunks.next().unwrap() == ['e'] 514 assert chunks.next().is_none() 515 ``` 516 """ 517 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()
519 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 520 """Return `True` if all items in the iterator match the predicate. 521 522 Example 523 ------- 524 ```py 525 iterator = Iter([1, 2, 3]) 526 while iterator.all(lambda item: isinstance(item, int)): 527 print("Still all integers") 528 continue 529 # Still all integers 530 ``` 531 532 Parameters 533 ---------- 534 predicate: `collections.Callable[[Item], bool]` 535 The function to test each item in the iterator. 536 """ 537 return all(predicate(item) for item in self)
Return True
if all items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
print("Still all integers")
continue
# Still all integers
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to test each item in the iterator.
539 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 540 """`True` if any items in the iterator match the predicate. 541 542 Example 543 ------- 544 ```py 545 iterator = Iter([1, 2, 3]) 546 if iterator.any(lambda item: isinstance(item, int)): 547 print("At least one item is an int.") 548 # At least one item is an int. 549 ``` 550 551 Parameters 552 ---------- 553 predicate: `collections.Callable[[Item], bool]` 554 The function to test each item in the iterator. 555 """ 556 return any(predicate(item) for item in self)
True
if any items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
print("At least one item is an int.")
# At least one item is an int.
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to test each item in the iterator.
558 def zip( 559 self, other: collections.Iterable[OtherItem] 560 ) -> Iter[tuple[Item, OtherItem]]: 561 """Zips the iterator with another iterable. 562 563 Example 564 ------- 565 ```py 566 iterator = Iter([1, 2, 3]) 567 for item, other_item in iterator.zip([4, 5, 6]): 568 assert item == other_item 569 <Iter([(1, 4), (2, 5), (3, 6)])> 570 ``` 571 572 Parameters 573 ---------- 574 other: `Iter[OtherItem]` 575 The iterable to zip with. 576 577 Returns 578 ------- 579 `Iter[tuple[Item, OtherItem]]` 580 The zipped iterator. 581 582 """ 583 return Iter(zip(self.raw_parts(), other))
Zips the iterator with another iterable.
Example
iterator = Iter([1, 2, 3])
for item, other_item in iterator.zip([4, 5, 6]):
assert item == other_item
<Iter([(1, 4), (2, 5), (3, 6)])>
Parameters
- other (
Iter[OtherItem]
): The iterable to zip with.
Returns
Iter[tuple[Item, OtherItem]]
: The zipped iterator.
585 def sort( 586 self, 587 *, 588 key: collections.Callable[[Item], _typeshed.SupportsRichComparison], 589 reverse: bool = False, 590 ) -> Iter[Item]: 591 """Sorts the iterator. 592 593 Example 594 ------- 595 ```py 596 iterator = Iter([3, 1, 6, 7]) 597 for item in iterator.sort(key=lambda item: item < 3): 598 print(item) 599 # 1 600 # 3 601 # 6 602 # 7 603 ``` 604 605 Parameters 606 ---------- 607 key: `collections.Callable[[Item], Any]` 608 The function to sort by. 609 reverse: `bool` 610 Whether to reverse the sort. 611 612 """ 613 return Iter(sorted(self.raw_parts(), key=key, reverse=reverse))
Sorts the iterator.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.sort(key=lambda item: item < 3):
print(item)
# 1
# 3
# 6
# 7
Parameters
- key (
collections.Callable[[Item], Any]
): The function to sort by. - reverse (
bool
): Whether to reverse the sort.
615 def reversed(self) -> Iter[Item]: 616 """Returns a new iterator that yields the items in the iterator in reverse order. 617 618 Example 619 ------- 620 ```py 621 iterator = Iter([3, 1, 6, 7]) 622 for item in iterator.reversed(): 623 print(item) 624 # 7 625 # 6 626 # 1 627 # 3 628 ``` 629 """ 630 # NOTE: In order to reverse the iterator we need to 631 # first collect it into some collection. 632 return Iter(reversed(self.collect()))
Returns a new iterator that yields the items in the iterator in reverse order.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.reversed():
print(item)
# 7
# 6
# 1
# 3
634 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 635 """Returns a new iterator that yields all items from both iterators. 636 637 Example 638 ------- 639 ```py 640 iterator = Iter([1, 2, 3]) 641 other = [4, 5, 6] 642 643 for item in iterator.union(other): 644 print(item) 645 # 1 646 # 2 647 # 3 648 # 4 649 # 5 650 # 6 651 ``` 652 653 Parameters 654 ---------- 655 other: `Iter[Item]` 656 The iterable to union with. 657 """ 658 return Iter(itertools.chain(self.raw_parts(), other))
Returns a new iterator that yields all items from both iterators.
Example
iterator = Iter([1, 2, 3])
other = [4, 5, 6]
for item in iterator.union(other):
print(item)
# 1
# 2
# 3
# 4
# 5
# 6
Parameters
- other (
Iter[Item]
): The iterable to union with.
660 def first(self) -> Option[Item]: 661 """Returns the first item in the iterator. 662 663 Example 664 ------- 665 ```py 666 iterator = Iter([3, 1, 6, 7]) 667 iterator.first().is_some_and(lambda x: x == 3) 668 ``` 669 """ 670 return self.take(1).next()
Returns the first item in the iterator.
Example
iterator = Iter([3, 1, 6, 7])
iterator.first().is_some_and(lambda x: x == 3)
672 def last(self) -> Option[Item]: 673 """Returns the last item in the iterator. 674 675 Example 676 ------- 677 ```py 678 iterator = Iter([3, 1, 6, 7]) 679 iterator.last().is_some_and(lambda x: x == 7) 680 ``` 681 """ 682 return self.reversed().first()
Returns the last item in the iterator.
Example
iterator = Iter([3, 1, 6, 7])
iterator.last().is_some_and(lambda x: x == 7)
684 def count(self) -> int: 685 """Return the count of elements in memory this iterator has. 686 687 Example 688 ------- 689 ```py 690 it = Iter(range(3)) 691 assert it.count() == 3 692 ``` 693 """ 694 count = 0 695 for _ in self: 696 count += 1 697 698 return count
Return the count of elements in memory this iterator has.
Example
it = Iter(range(3))
assert it.count() == 3
700 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 701 """Searches for an element of an iterator that satisfies a predicate. 702 703 If you want the position of the element, use `Iterator.position` instead. 704 705 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 706 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 707 708 Example 709 ------- 710 ```py 711 it = Iter(range(10)) 712 item = it.find(lambda num: num > 5) 713 print(item) # 6 714 ``` 715 """ 716 for item in self: 717 if predicate(item): 718 return _option.Some(item) 719 720 # no more items 721 return _option.NOTHING # pyright: ignore
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
723 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 724 """Searches for the position of an element in the iterator that satisfies a predicate. 725 726 If you want the object itself, use `Iterator.find` instead. 727 728 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 729 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 730 731 Example 732 ------- 733 ```py 734 it = Iter(range(10)) 735 position = it.find(lambda num: num > 5) 736 assert position.unwrap() == 6 737 ``` 738 """ 739 for position, value in self.enumerate(): 740 if predicate(value): 741 return _option.Some(position) 742 743 # no more items 744 return _option.NOTHING # pyright: ignore
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
746 def fold( 747 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 748 ) -> OtherItem: 749 """Folds every element into an accumulator by applying an operation, returning the final result. 750 751 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 752 The closure returns the value that the accumulator should have for the next iteration. 753 754 The initial value is the value the accumulator will have on the first call. 755 756 After applying this closure to every element of the iterator, fold() returns the accumulator. 757 758 This operation is sometimes called ‘reduce’ or ‘inject’. 759 760 Example 761 ------- 762 ```py 763 a = Iter([1, 2, 3, 4]) 764 sum = a.fold(0, lambda acc, elem: acc + elem) 765 assert sum == 10 766 ``` 767 """ 768 accum = init 769 while True: 770 try: 771 x = self.__next__() 772 accum = f(accum, x) 773 except StopIteration: 774 break 775 776 return accum
Folds every element into an accumulator by applying an operation, returning the final result.
fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. The closure returns the value that the accumulator should have for the next iteration.
The initial value is the value the accumulator will have on the first call.
After applying this closure to every element of the iterator, fold() returns the accumulator.
This operation is sometimes called ‘reduce’ or ‘inject’.
Example
a = Iter([1, 2, 3, 4])
sum = a.fold(0, lambda acc, elem: acc + elem)
assert sum == 10
778 def sum(self: Sum) -> int: 779 """Sums an iterator of a possible type that can be converted to an integer. 780 781 Example 782 ------- 783 ```py 784 numbers: Iterator[str] = Iter(["1", "2", "3"]) 785 total = numbers.sum() 786 assert total == 6 787 ``` 788 """ 789 return sum(int(_) for _ in self)
Sums an iterator of a possible type that can be converted to an integer.
Example
numbers: Iterator[str] = Iter(["1", "2", "3"])
total = numbers.sum()
assert total == 6
791 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 792 """Calls `func` on each item in the iterator. 793 794 Example 795 ------- 796 ```py 797 iterator = Iter([1, 2, 3]) 798 iterator.for_each(lambda item: print(item)) 799 # 1 800 # 2 801 # 3 802 ``` 803 804 Parameters 805 ---------- 806 func: `collections.Callable[[Item], typing.Any]` 807 The function to call on each item in the iterator. 808 """ 809 for item in self: 810 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.
812 async def async_for_each( 813 self, 814 func: collections.Callable[ 815 [Item], collections.Coroutine[None, typing.Any, OtherItem] 816 ], 817 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 818 """Calls the async function on each item in the iterator concurrently. 819 820 Example 821 ------- 822 ```py 823 async def create_user(username: str) -> None: 824 async with aiohttp.request("POST", f'.../{username}') as r: 825 return await r.json() 826 827 async def main(): 828 users = sain.into_iter(["danny", "legalia"]) 829 results = await users.async_for_each(lambda username: create_user(username)) 830 for k, v in results.unwrap().items(): 831 ... 832 ``` 833 834 Parameters 835 ---------- 836 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 837 The async function to call on each item in the iterator. 838 """ 839 return await futures.join(*(func(item) for item in self))
Calls the async function on each item in the iterator concurrently.
Example
async def create_user(username: str) -> None:
async with aiohttp.request("POST", f'.../{username}') as r:
return await r.json()
async def main():
users = sain.into_iter(["danny", "legalia"])
results = await users.async_for_each(lambda username: create_user(username))
for k, v in results.unwrap().items():
...
Parameters
- func (
collections.Callable[[Item], Coroutine[None, Any, Any]]
): The async function to call on each item in the iterator.
473@rustc_diagnostic_item("todo") 474def todo(message: typing.LiteralString | None = None) -> typing.NoReturn: 475 """A place holder that indicates unfinished code. 476 477 Example 478 ------- 479 ```py 480 from sain import todo 481 482 def from_json(payload: dict[str, int]) -> int: 483 # Calling this function will raise `Error`. 484 todo() 485 ``` 486 487 Parameters 488 ---------- 489 message : `str | None` 490 Multiple optional arguments to pass if the error was raised. 491 """ 492 raise Error(f"not yet implemented: {message}" if message else "not yet implemented")
A place holder that indicates unfinished code.
Example
from sain import todo
def from_json(payload: dict[str, int]) -> int:
# Calling this function will raise `Error`.
todo()
Parameters
- message (
str | None
): Multiple optional arguments to pass if the error was raised. - # Implementations
- **This function implements todo:
388@rustc_diagnostic_item("deprecated") 389def deprecated( 390 *, 391 since: typing.Literal["CURRENT_VERSION"] | typing.LiteralString | None = None, 392 removed_in: typing.LiteralString | None = None, 393 use_instead: typing.LiteralString | None = None, 394 hint: typing.LiteralString | None = None, 395) -> collections.Callable[ 396 [collections.Callable[P, U]], 397 collections.Callable[P, U], 398]: 399 """A decorator that marks a function as deprecated. 400 401 An attempt to call the object that's marked will cause a runtime warn. 402 403 Example 404 ------- 405 ```py 406 from sain import deprecated 407 408 @deprecated( 409 since = "1.0.0", 410 removed_in ="3.0.0", 411 use_instead = "UserImpl()", 412 hint = "Hint for ux." 413 ) 414 class User: 415 ... 416 417 user = User() # This will cause a warning at runtime. 418 ``` 419 420 Parameters 421 ---------- 422 since : `str` 423 The version that the function was deprecated. the `CURRENT_VERSION` is used internally only. 424 removed_in : `str | None` 425 If provided, It will log when will the object will be removed in. 426 use_instead : `str | None` 427 If provided, This should be the alternative object name that should be used instead. 428 hint: `str` 429 An optional hint for the user. 430 """ 431 432 def _create_message(obj: typing.Any) -> str: 433 msg = f"{_obj_type(obj)} `{obj.__module__}.{obj.__name__}` is deprecated." 434 435 if since is not None: 436 if since == "CURRENT_VERSION": 437 from sain import __version__ as _version 438 439 msg += " since " + _version 440 else: 441 msg += " since " + since 442 443 if removed_in: 444 msg += f" Scheduled for removal in `{removed_in}`." 445 446 if use_instead is not None: 447 msg += f" Use `{use_instead}` instead." 448 449 if hint: 450 msg += f" Hint: {hint}" 451 return msg 452 453 def decorator(func: collections.Callable[P, U]) -> collections.Callable[P, U]: 454 message = _create_message(func) 455 456 @functools.wraps(func) 457 def wrapper(*args: P.args, **kwargs: P.kwargs) -> U: 458 _warn(message) 459 return func(*args, **kwargs) 460 461 # idk why pyright doesn't know the type of wrapper. 462 m = f"\n# Warning ⚠️\n{message}." 463 if wrapper.__doc__: 464 # append this message to an existing document. 465 wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}" 466 else: 467 wrapper.__doc__ = m 468 return wrapper 469 470 return decorator
A decorator that marks a function as deprecated.
An attempt to call the object that's marked will cause a runtime warn.
Example
from sain import deprecated
@deprecated(
since = "1.0.0",
removed_in ="3.0.0",
use_instead = "UserImpl()",
hint = "Hint for ux."
)
class User:
...
user = User() # This will cause a warning at runtime.
Parameters
- since (
str
): The version that the function was deprecated. 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:
495@rustc_diagnostic_item("unimplemented") 496def unimplemented( 497 *, 498 message: typing.LiteralString | None = None, 499 available_in: typing.LiteralString | None = None, 500) -> collections.Callable[ 501 [collections.Callable[P, U]], 502 collections.Callable[P, U], 503]: 504 """A decorator that marks an object as unimplemented. 505 506 An attempt to call the object that's marked will cause a runtime warn. 507 508 Example 509 ------- 510 ```py 511 from sain import unimplemented 512 513 @unimplemented("User object is not implemented yet.") 514 class User: 515 ... 516 ``` 517 518 Parameters 519 ---------- 520 message : `str | None` 521 An optional message to be displayed when the function is called. Otherwise default message will be used. 522 available_in : `str | None` 523 If provided, This will be shown as what release this object be implemented. 524 """ 525 526 def _create_message(obj: typing.Any) -> str: 527 msg = ( 528 message 529 or f"{_obj_type(obj)} `{obj.__module__}.{obj.__name__}` is not yet implemented." 530 ) # noqa: W503 531 532 if available_in: 533 msg += f" Available in `{available_in}`." 534 return msg 535 536 def decorator(obj: collections.Callable[P, U]) -> collections.Callable[P, U]: 537 message = _create_message(obj) 538 539 @functools.wraps(obj) 540 def wrapper(*args: P.args, **kwargs: P.kwargs) -> U: 541 _warn(message) 542 return obj(*args, **kwargs) 543 544 # idk why pyright doesn't know the type of wrapper. 545 m = f"\n# Warning ⚠️\n{message}." 546 if wrapper.__doc__: 547 # append this message to an existing document. 548 wrapper.__doc__ = inspect.cleandoc(wrapper.__doc__) + f"{m}" 549 else: 550 wrapper.__doc__ = m 551 return wrapper 552 553 return decorator
A decorator that marks an object as unimplemented.
An attempt to call the object that's marked will cause a runtime warn.
Example
from sain import unimplemented
@unimplemented("User object is not implemented yet.")
class User:
...
Parameters
- message (
str | None
): An optional message to be displayed when the function is called. Otherwise default message will be used. - available_in (
str | None
): If provided, This will be shown as what release this object be implemented. - # Implementations
- **This function implements unimplemented:
556@rustc_diagnostic_item("doc") 557def doc( 558 path: Read, 559) -> collections.Callable[ 560 [collections.Callable[P, U]], 561 collections.Callable[P, U], 562]: 563 """Set `path` to be the object's documentation. 564 565 Example 566 ------- 567 ```py 568 from sain import doc 569 from pathlib import Path 570 571 @doc(Path("../README.md")) 572 class User: 573 574 @doc("bool.html") 575 def bool_docs() -> None: 576 ... 577 ``` 578 579 Parameters 580 ---------- 581 path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]` 582 The path to read the content from. 583 """ 584 585 def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]: 586 with open(path, "r") as file: 587 f.__doc__ = file.read() 588 589 @functools.wraps(f) 590 def wrapper(*args: P.args, **kwargs: P.kwargs) -> U: 591 return f(*args, **kwargs) 592 593 return wrapper 594 595 return decorator
Set path
to be the object's documentation.
Example
from sain import doc
from pathlib import Path
@doc(Path("../README.md"))
class User:
@doc("bool.html")
def bool_docs() -> None:
...
Parameters
- path (
type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]
): The path to read the content from. - # Implementations
- **This function implements doc:
297@rustc_diagnostic_item("include_str") 298def include_str(file: typing.LiteralString) -> typing.LiteralString: 299 """Includes a file as literal `str`. 300 301 This function is not magic like Rust's, It is literally defined as 302 303 ```py 304 with open(file, "r") as f: 305 return f.read() 306 ``` 307 308 The file name can may be either a relative to the current file or a complate path. 309 310 Example 311 ------- 312 ```py 313 from sain.macros import include_str 314 315 def entry() -> None: 316 ... 317 318 entry.__doc__ = include_str("README.md") 319 320 ``` 321 """ 322 with open(file, "r") as buf: 323 return buf.read() # pyright: ignore - simulates a `&'static str` slice.
Includes a file as literal str
.
This function is not magic like Rust's, 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 complate 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.
267@rustc_diagnostic_item("include_bytes") 268def include_bytes(file: typing.LiteralString) -> bytes: 269 """Includes a file as `bytes`. 270 271 This function is not magic like Rust's, It is literally defined as 272 273 ```py 274 with open(file, "rb") as f: 275 return f.read() 276 ``` 277 278 The file name can may be either a relative to the current file or a complate path. 279 280 Example 281 ------- 282 File "spanish.in": 283 ```text 284 adiós 285 ``` 286 File "main.py": 287 ```py 288 from sain.macros import include_bytes 289 buffer = include_bytes("spanish.in") 290 assert buffer.decode() == "adiós" 291 ``` 292 """ 293 with open(file, "rb") as buf: 294 return buf.read()
Includes a file as bytes
.
This function is not magic like Rust's, 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 complate 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.
227@rustc_diagnostic_item("assert_eq") 228def assert_eq(left: T, right: T) -> None: 229 """Asserts that two expressions are equal to each other. 230 231 This exactly as `assert left == right`, but includes a useful message incase of failure. 232 233 Example 234 ------- 235 ```py 236 from sain.macros import assert_eq 237 a = 3 238 b = 1 + 2 239 assert_eq(a, b) 240 ``` 241 """ 242 assert ( 243 left == right 244 ), f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"'
Asserts that two expressions are equal to each other.
This exactly as assert left == right
, but includes a useful message incase 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.
247@rustc_diagnostic_item("assert_ne") 248def assert_ne(left: T, right: T) -> None: 249 """Asserts that two expressions are not equal to each other. 250 251 This exactly as `assert left == right`, but includes a useful message incase of failure. 252 253 Example 254 ------- 255 ```py 256 from sain.macros import assert_ne 257 a = 3 258 b = 2 + 2 259 assert_ne(a, b) 260 ``` 261 """ 262 assert ( 263 left != right 264 ), f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"'
Asserts that two expressions are not equal to each other.
This exactly as assert left == right
, but includes a useful message incase 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.
119@rustc_diagnostic_item("Ok") 120@typing.final 121@dataclasses.dataclass(slots=True, frozen=True, repr=False) 122class Ok(typing.Generic[T]): 123 """Contains the success value of `Result[T, ...]`.""" 124 125 _inner: T 126 127 ############################### 128 # * Querying operations. * # 129 ############################### 130 131 def is_ok(self) -> typing.Literal[True]: 132 """Returns `True` if the contained value is `Ok` and `False` if it an `Err`. 133 134 Example 135 ------- 136 ```py 137 value: Result[str, None] = Ok("value") 138 assert value.is_ok() == True 139 ``` 140 """ 141 return True 142 143 def is_ok_and(self, f: F[T, bool]) -> bool: 144 """Returns `True` if the contained value is `Ok` and `f()` returns True. 145 146 Example 147 ------- 148 ```py 149 value: Result[str, None] = Ok("value") 150 assert value.is_ok_and(lambda inner: inner == "value") 151 # True 152 ``` 153 """ 154 return f(self._inner) 155 156 # These are never truthy in an `Ok` instance. 157 def is_err(self) -> typing.Literal[False]: 158 """Returns `True` if the contained value is `Err`. 159 160 Example 161 ------- 162 ```py 163 value: Result[str, None] = Ok("value") 164 165 assert value.is_err() == False 166 ``` 167 """ 168 return False 169 170 def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]: 171 """Returns `True` if the contained value is `Ok` and `f()` returns True. 172 173 Example 174 ------- 175 ```py 176 value: Result[str, None] = Ok("value") 177 178 assert value.is_err_and(lambda inner: inner == "value") 179 # False 180 ``` 181 """ 182 return False 183 184 ################### 185 # * Extractors * # 186 ################### 187 188 def expect(self, message: str, /) -> T: 189 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 190 if it was `Err` with `message` passed to it. 191 192 Example 193 ------- 194 ```py 195 ok: Result[str, None] = Ok("owo") 196 ok.expect("err") # owo 197 198 err: Result[str, None] = Err(None) 199 err.expect("err") # RuntimeError("err") 200 ``` 201 """ 202 return self._inner 203 204 def expect_err(self) -> Never: 205 """Return the `Err` value if `self` is an `Err`, panicking otherwise. 206 207 Example 208 ------- 209 ```py 210 ok: Result[str, None] = Ok("owo") 211 ok.expect_err() # RuntimeError("Called expect_err on `Ok`) 212 213 err: Result[str, None] = Err(None) 214 err.expect_err() # None 215 ``` 216 """ 217 raise RuntimeError("Called `expect_err` on an `Ok` value.") 218 219 def unwrap(self) -> T: 220 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 221 222 Example 223 ------- 224 ```py 225 ok: Result[str, None] = Ok("owo") 226 ok.unwrap() # owo 227 228 err: Result[str, None] = Err(None) 229 err.unwrap() # RuntimeError 230 ``` 231 """ 232 return self._inner 233 234 def unwrap_or(self, default: T, /) -> T: 235 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 236 237 Example 238 ------- 239 ```py 240 ok: Result[str, None] = Ok("OwO") 241 ok.unwrap_or("uwu") # OwO 242 243 err: Result[str, None] = Err(None) 244 err.unwrap_or("uwu") # uwu 245 ``` 246 """ 247 return self._inner 248 249 def unwrap_or_else(self, f: F[E, T]) -> T: 250 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 251 252 Example 253 ------- 254 ```py 255 ok: Result[int, str] = Ok(4) 256 ok.unwrap_or_else(lambda e: 0) # 4 257 258 err: Result[int, str] = Err("word") 259 err.unwrap_or_else(lambda e: len(e)) # 4 260 ``` 261 """ 262 return self._inner 263 264 def unwrap_err(self) -> Never: 265 """Return the contained `Err` value, Raising if it was `Ok`. 266 267 Example 268 ------- 269 ```py 270 ok: Result[str, None] = Ok("buh") 271 ok.unwrap_err() # RuntimeError 272 273 err: Result[str, None] = Err(None) 274 err.unwrap_err() == None 275 # True 276 ``` 277 """ 278 raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}") 279 280 ############################ 281 # * Conversion adapters. * # 282 ############################ 283 284 def ok(self) -> Option[T]: 285 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 286 287 Example 288 ------- 289 ```py 290 value: Result[str, None] = Ok("buh") 291 value.ok().is_some() # True 292 293 value: Result[str, int] = Err(0) 294 value.ok().is_none() # True 295 ``` 296 """ 297 return _option.Some(self._inner) 298 299 def err(self) -> Option[T]: 300 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 301 302 Example 303 ------- 304 ```py 305 value: Result[str, None] = Ok("buh") 306 value.err().is_none() # True 307 308 value: Result[str, int] = Err(0) 309 value.err().is_some() # True 310 ``` 311 """ 312 return _option.NOTHING # pyright: ignore 313 314 def inspect(self, f: F[T, typing.Any]) -> Self: 315 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 316 317 Example 318 ------- 319 ```py 320 def sink(value: str) -> None: 321 # do something with value 322 print("Called " + value) 323 324 x: Result[str, None] = Ok("ok") 325 x.inspect(sink) # "Called ok" 326 327 x: Result[str, str] = Err("err") 328 x.inspect(sink) # None 329 ``` 330 """ 331 f(self._inner) 332 return self 333 334 def inspect_err(self, f: F[E, typing.Any]) -> Self: 335 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 336 337 Example 338 ------- 339 ```py 340 def sink(value: str) -> None: 341 # do something with value 342 print("Called " + value) 343 344 x: Result[str, None] = Ok("ok") 345 x.inspect_err(sink) # None 346 347 x: Result[str, str] = Err("err") 348 x.inspect_err(sink) # Called err 349 ``` 350 """ 351 return self 352 353 def map(self, f: F[T, U], /) -> Ok[U]: 354 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 355 leaving `Err` untouched. 356 357 Example 358 ------- 359 ```py 360 ok: Result[str, int] = Ok("1") 361 ok.map(lambda c: int(c) + 1) # Ok(2) 362 363 err: Result[str, int] = Err(0) 364 err.map(str.upper) # Err(0) 365 ``` 366 """ 367 return Ok(f(self._inner)) 368 369 def map_or(self, f: F[T, U], default: U, /) -> U: 370 """Returns the provided `default` if the contained value is `Err`, 371 372 Otherwise extracts the `Ok` value and maps it to `f()` 373 374 Example 375 ------- 376 ```py 377 x: Result[str, str] = Ok("foo") 378 assert x.map_or(lambda c: len(c), 42) == 3 379 380 x: Result[str, str] = Err("bar") 381 assert x.map_or(lambda c: len(c), 42) == 42 382 ``` 383 """ 384 return f(self._inner) 385 386 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 387 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 388 or function `f` to a contained Ok value. 389 390 Example 391 ------- 392 ```py 393 x: Result[str, str] = Ok("four") 394 assert x.map_or_else( 395 lambda ok: 2 * len(ok), 396 default=lambda err: len(err) 397 ) == 8 398 399 x: Result[str, str] = Err("bar") 400 assert x.map_or_else( 401 lambda c: 2 * len(c), 402 lambda err: len(err) 403 ) == 3 404 ``` 405 """ 406 return f(self._inner) 407 408 def map_err(self, f: F[E, U], /) -> Self: 409 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 410 411 Example 412 ------- 413 ```py 414 x: Result[str, int] = Ok("blue") 415 x.map_err(lambda err: err + 1) # Ok("blue") 416 417 x: Result[str, int] = Err(5) 418 x.map_err(float) # Err(5.0) 419 ``` 420 """ 421 return self 422 423 ############################## 424 # * Iterator constructors. * # 425 ############################## 426 427 def iter(self) -> _iter.TrustedIter[T]: 428 """An iterator over the possible contained value. 429 430 If `self` was `Ok`, then the iterator will yield the Ok `T`. otherwise yields nothing. 431 432 Example 433 ------- 434 ```py 435 c: Result[str, int] = Ok("blue") 436 c.iter().next() == Some("blue") 437 438 c: Result[str, int] = Err(0) 439 c.iter().next() == Some(None) 440 ``` 441 """ 442 return _iter.TrustedIter((self._inner,)) 443 444 def __iter__(self) -> collections.Iterator[T]: 445 yield self._inner 446 447 ################# 448 # * Overloads * # 449 ################# 450 451 def __repr__(self) -> str: 452 return f"Ok({self._inner!r})" 453 454 def __or__(self, other: T) -> T: 455 return self._inner 456 457 def __invert__(self) -> T: 458 return self._inner
Contains the success value of Result[T, ...]
.
Implementations
143 def is_ok_and(self, f: F[T, bool]) -> bool: 144 """Returns `True` if the contained value is `Ok` and `f()` returns True. 145 146 Example 147 ------- 148 ```py 149 value: Result[str, None] = Ok("value") 150 assert value.is_ok_and(lambda inner: inner == "value") 151 # True 152 ``` 153 """ 154 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
157 def is_err(self) -> typing.Literal[False]: 158 """Returns `True` if the contained value is `Err`. 159 160 Example 161 ------- 162 ```py 163 value: Result[str, None] = Ok("value") 164 165 assert value.is_err() == False 166 ``` 167 """ 168 return False
Returns True
if the contained value is Err
.
Example
value: Result[str, None] = Ok("value")
assert value.is_err() == False
170 def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]: 171 """Returns `True` if the contained value is `Ok` and `f()` returns True. 172 173 Example 174 ------- 175 ```py 176 value: Result[str, None] = Ok("value") 177 178 assert value.is_err_and(lambda inner: inner == "value") 179 # False 180 ``` 181 """ 182 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
188 def expect(self, message: str, /) -> T: 189 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 190 if it was `Err` with `message` passed to it. 191 192 Example 193 ------- 194 ```py 195 ok: Result[str, None] = Ok("owo") 196 ok.expect("err") # owo 197 198 err: Result[str, None] = Err(None) 199 err.expect("err") # RuntimeError("err") 200 ``` 201 """ 202 return self._inner
204 def expect_err(self) -> Never: 205 """Return the `Err` value if `self` is an `Err`, panicking otherwise. 206 207 Example 208 ------- 209 ```py 210 ok: Result[str, None] = Ok("owo") 211 ok.expect_err() # RuntimeError("Called expect_err on `Ok`) 212 213 err: Result[str, None] = Err(None) 214 err.expect_err() # None 215 ``` 216 """ 217 raise RuntimeError("Called `expect_err` on an `Ok` value.")
219 def unwrap(self) -> T: 220 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 221 222 Example 223 ------- 224 ```py 225 ok: Result[str, None] = Ok("owo") 226 ok.unwrap() # owo 227 228 err: Result[str, None] = Err(None) 229 err.unwrap() # RuntimeError 230 ``` 231 """ 232 return self._inner
234 def unwrap_or(self, default: T, /) -> T: 235 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 236 237 Example 238 ------- 239 ```py 240 ok: Result[str, None] = Ok("OwO") 241 ok.unwrap_or("uwu") # OwO 242 243 err: Result[str, None] = Err(None) 244 err.unwrap_or("uwu") # uwu 245 ``` 246 """ 247 return self._inner
249 def unwrap_or_else(self, f: F[E, T]) -> T: 250 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 251 252 Example 253 ------- 254 ```py 255 ok: Result[int, str] = Ok(4) 256 ok.unwrap_or_else(lambda e: 0) # 4 257 258 err: Result[int, str] = Err("word") 259 err.unwrap_or_else(lambda e: len(e)) # 4 260 ``` 261 """ 262 return self._inner
264 def unwrap_err(self) -> Never: 265 """Return the contained `Err` value, Raising if it was `Ok`. 266 267 Example 268 ------- 269 ```py 270 ok: Result[str, None] = Ok("buh") 271 ok.unwrap_err() # RuntimeError 272 273 err: Result[str, None] = Err(None) 274 err.unwrap_err() == None 275 # True 276 ``` 277 """ 278 raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}")
284 def ok(self) -> Option[T]: 285 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 286 287 Example 288 ------- 289 ```py 290 value: Result[str, None] = Ok("buh") 291 value.ok().is_some() # True 292 293 value: Result[str, int] = Err(0) 294 value.ok().is_none() # True 295 ``` 296 """ 297 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
299 def err(self) -> Option[T]: 300 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 301 302 Example 303 ------- 304 ```py 305 value: Result[str, None] = Ok("buh") 306 value.err().is_none() # True 307 308 value: Result[str, int] = Err(0) 309 value.err().is_some() # True 310 ``` 311 """ 312 return _option.NOTHING # pyright: ignore
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
314 def inspect(self, f: F[T, typing.Any]) -> Self: 315 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 316 317 Example 318 ------- 319 ```py 320 def sink(value: str) -> None: 321 # do something with value 322 print("Called " + value) 323 324 x: Result[str, None] = Ok("ok") 325 x.inspect(sink) # "Called ok" 326 327 x: Result[str, str] = Err("err") 328 x.inspect(sink) # None 329 ``` 330 """ 331 f(self._inner) 332 return self
334 def inspect_err(self, f: F[E, typing.Any]) -> Self: 335 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 336 337 Example 338 ------- 339 ```py 340 def sink(value: str) -> None: 341 # do something with value 342 print("Called " + value) 343 344 x: Result[str, None] = Ok("ok") 345 x.inspect_err(sink) # None 346 347 x: Result[str, str] = Err("err") 348 x.inspect_err(sink) # Called err 349 ``` 350 """ 351 return self
353 def map(self, f: F[T, U], /) -> Ok[U]: 354 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 355 leaving `Err` untouched. 356 357 Example 358 ------- 359 ```py 360 ok: Result[str, int] = Ok("1") 361 ok.map(lambda c: int(c) + 1) # Ok(2) 362 363 err: Result[str, int] = Err(0) 364 err.map(str.upper) # Err(0) 365 ``` 366 """ 367 return Ok(f(self._inner))
369 def map_or(self, f: F[T, U], default: U, /) -> U: 370 """Returns the provided `default` if the contained value is `Err`, 371 372 Otherwise extracts the `Ok` value and maps it to `f()` 373 374 Example 375 ------- 376 ```py 377 x: Result[str, str] = Ok("foo") 378 assert x.map_or(lambda c: len(c), 42) == 3 379 380 x: Result[str, str] = Err("bar") 381 assert x.map_or(lambda c: len(c), 42) == 42 382 ``` 383 """ 384 return f(self._inner)
386 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 387 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 388 or function `f` to a contained Ok value. 389 390 Example 391 ------- 392 ```py 393 x: Result[str, str] = Ok("four") 394 assert x.map_or_else( 395 lambda ok: 2 * len(ok), 396 default=lambda err: len(err) 397 ) == 8 398 399 x: Result[str, str] = Err("bar") 400 assert x.map_or_else( 401 lambda c: 2 * len(c), 402 lambda err: len(err) 403 ) == 3 404 ``` 405 """ 406 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
408 def map_err(self, f: F[E, U], /) -> Self: 409 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 410 411 Example 412 ------- 413 ```py 414 x: Result[str, int] = Ok("blue") 415 x.map_err(lambda err: err + 1) # Ok("blue") 416 417 x: Result[str, int] = Err(5) 418 x.map_err(float) # Err(5.0) 419 ``` 420 """ 421 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)
427 def iter(self) -> _iter.TrustedIter[T]: 428 """An iterator over the possible contained value. 429 430 If `self` was `Ok`, then the iterator will yield the Ok `T`. otherwise yields nothing. 431 432 Example 433 ------- 434 ```py 435 c: Result[str, int] = Ok("blue") 436 c.iter().next() == Some("blue") 437 438 c: Result[str, int] = Err(0) 439 c.iter().next() == Some(None) 440 ``` 441 """ 442 return _iter.TrustedIter((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)
461@rustc_diagnostic_item("Err") 462@typing.final 463@dataclasses.dataclass(slots=True, frozen=True, repr=False) 464class Err(typing.Generic[E]): 465 """Contains the error value of `Result[..., E]`.""" 466 467 _inner: E 468 469 ################################ 470 # * Boolean operations. * # 471 ################################ 472 473 def is_ok(self) -> typing.Literal[False]: 474 """Returns `True` if the contained value is `Ok` and `False` if it an `Err`. 475 476 Example 477 ------- 478 ```py 479 value: Result[str, None] = Err(None) 480 481 assert value.is_ok() == False 482 ``` 483 """ 484 return False 485 486 def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]: 487 """Returns `True` if the contained value is `Ok` and `f()` returns True. 488 489 Example 490 ------- 491 ```py 492 value: Result[str, None] = Err(None) 493 494 assert value.is_ok_and(lambda inner: inner == "value") 495 # False 496 ``` 497 """ 498 return False 499 500 # These are never truthy in an `Ok` instance. 501 def is_err(self) -> typing.Literal[True]: 502 """Returns `True` if the contained value is `Err`. 503 504 Example 505 ------- 506 ```py 507 value: Result[str, None] = Err(None) 508 509 assert value.is_err() == True 510 ``` 511 """ 512 return True 513 514 def is_err_and(self, f: F[E, bool]) -> bool: 515 """Returns `True` if the contained value is `Ok` and `f()` returns True.. 516 517 Example 518 ------- 519 ```py 520 value: Result[str, None] = Err(None) 521 522 assert value.is_err_and(lambda err: err is None) 523 # True 524 ``` 525 """ 526 return f(self._inner) 527 528 ################### 529 # * Extractors. * # 530 ################### 531 532 def expect(self, msg: str) -> Never: 533 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 534 if it was `Err` with `message` passed to it. 535 536 Example 537 ------- 538 ```py 539 ok: Result[str, None] = Ok("owo") 540 ok.expect("err") # owo 541 542 err: Result[str, None] = Err(None) 543 err.expect("err") # RuntimeError("err") 544 ``` 545 """ 546 raise RuntimeError(msg) from None 547 548 def expect_err(self) -> E: 549 """Return the `Err` if it was `Err`, panicking otherwise. 550 551 552 Example 553 ------- 554 ```py 555 x: Result[str, None] = Ok("owo") 556 x.expect_err() # RuntimeError("Called expect_err on `Ok`) 557 558 x: Result[str, None] = Err(None) 559 x.expect_err() # None 560 ``` 561 """ 562 return self._inner 563 564 def unwrap(self) -> Never: 565 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 566 567 Example 568 ------- 569 ```py 570 ok: Result[str, None] = Ok("owo") 571 ok.unwrap() # owo 572 573 err: Result[str, None] = Err(None) 574 err.unwrap() # RuntimeError 575 ``` 576 """ 577 raise RuntimeError( 578 f"Called `unwrap()` on an `Err` variant: {self._inner!r}" 579 ) from None 580 581 def unwrap_or(self, default: T, /) -> T: 582 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 583 584 Example 585 ------- 586 ```py 587 ok: Result[str, None] = Ok("OwO") 588 ok.unwrap_or("uwu") # OwO 589 590 err: Result[str, None] = Err(None) 591 err.unwrap_or("uwu") # uwu 592 ``` 593 """ 594 return default 595 596 def unwrap_or_else(self, f: F[E, T]) -> T: 597 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 598 599 Example 600 ------- 601 ```py 602 ok: Result[int, str] = Ok(4) 603 ok.unwrap_or_else(lambda e: 0) # 4 604 605 err: Result[int, str] = Err("word") 606 err.unwrap_or_else(lambda e: len(e)) # 4 607 ``` 608 """ 609 return f(self._inner) 610 611 def unwrap_err(self) -> E: 612 """Return the contained `Err` value, Raising if it was `Ok`. 613 614 Example 615 ------- 616 ```py 617 ok: Result[str, None] = Ok("buh") 618 ok.unwrap_err() # RuntimeError 619 620 err: Result[str, None] = Err(None) 621 err.unwrap_err() == None 622 # True 623 ``` 624 """ 625 return self._inner 626 627 ############################ 628 # * Conversion adapters. * # 629 ############################ 630 631 def inspect(self, f: F[T, typing.Any]) -> Self: 632 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 633 634 Example 635 ------- 636 ```py 637 def sink(value: str) -> None: 638 # do something with value 639 print("Called " + value) 640 641 x: Result[str, None] = Ok("ok") 642 x.inspect(sink) # "Called ok" 643 644 x: Result[str, str] = Err("err") 645 x.inspect(sink) # None 646 ``` 647 """ 648 return self 649 650 def inspect_err(self, f: F[E, typing.Any]) -> Self: 651 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 652 653 Example 654 ------- 655 ```py 656 def sink(value: str) -> None: 657 # do something with value 658 print("Called " + value) 659 660 x: Result[str, None] = Ok("ok") 661 x.inspect_err(sink) # None 662 663 x: Result[str, str] = Err("err") 664 x.inspect_err(sink) # Called err 665 ``` 666 """ 667 f(self._inner) 668 return self 669 670 def ok(self) -> Option[None]: 671 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 672 673 Example 674 ------- 675 ```py 676 value: Result[str, None] = Ok("buh") 677 value.ok().is_some() # True 678 679 value: Result[str, int] = Err(0) 680 value.ok().is_none() # True 681 ``` 682 """ 683 return _option.NOTHING 684 685 def err(self) -> Option[E]: 686 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 687 688 Example 689 ------- 690 ```py 691 value: Result[str, None] = Ok("buh") 692 value.err().is_none() # True 693 694 value: Result[str, int] = Err(0) 695 value.err().is_some() # True 696 ``` 697 """ 698 return _option.Some(self._inner) 699 700 def map(self, f: F[E, U]) -> Self: 701 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 702 leaving `Err` untouched. 703 704 Example 705 ------- 706 ```py 707 ok: Result[str, int] = Ok("1") 708 ok.map(lambda c: int(c) + 1) # Ok(2) 709 710 err: Result[str, int] = Err(0) 711 err.map(str.upper) # Err(0) 712 ``` 713 """ 714 return self 715 716 def map_or(self, f: F[E, U], default: U, /) -> U: 717 """Returns the provided `default` if the contained value is `Err`, 718 719 Otherwise extracts the `Ok` value and maps it to `f()` 720 721 Example 722 ------- 723 ```py 724 x: Result[str, str] = Ok("foo") 725 assert x.map_or(lambda c: len(c), 42) == 3 726 727 x: Result[str, str] = Err("bar") 728 assert x.map_or(lambda c: len(c), 42) == 42 729 ``` 730 """ 731 return default 732 733 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 734 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 735 or function `f` to a contained Ok value. 736 737 Example 738 ------- 739 ```py 740 x: Result[str, str] = Ok("four") 741 assert x.map_or_else( 742 lambda ok: 2 * len(ok), 743 default=lambda err: len(err) 744 ) == 8 745 746 x: Result[str, str] = Err("bar") 747 assert x.map_or_else( 748 lambda c: 2 * len(c), 749 lambda err: len(err) 750 ) == 3 751 ``` 752 """ 753 return default(self._inner) 754 755 def map_err(self, f: F[E, U]) -> Err[U]: 756 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 757 758 Example 759 ------- 760 ```py 761 x: Result[str, int] = Ok("blue") 762 x.map_err(lambda err: err + 1) # Ok("blue") 763 764 x: Result[str, int] = Err(5) 765 x.map_err(float) # Err(5.0) 766 ``` 767 """ 768 return Err(f(self._inner)) 769 770 ############################## 771 # * Iterator constructors. * # 772 ############################## 773 774 def iter(self) -> _iter.Empty[Never]: 775 """An iterator over the possible contained value. 776 777 If `self` was `Ok`, then the iterator will yield `T`, otherwise yields nothing. 778 779 Example 780 ------- 781 ```py 782 c: Result[str, int] = Ok("blue") 783 c.iter().next() == Some("blue") 784 785 c: Result[str, int] = Err(0) 786 c.iter().next() == Some(None) 787 ``` 788 """ 789 return _iter.Empty() 790 791 def __iter__(self) -> collections.Iterator[Never]: 792 yield from () 793 794 ################# 795 # * Overloads * # 796 ################# 797 798 def __repr__(self) -> str: 799 return f"Err({self._inner!r})" 800 801 def __or__(self, other: T) -> T: 802 return other 803 804 def __invert__(self) -> Never: 805 self.unwrap()
Contains the error value of Result[..., E]
.
Implementations
This class implements .Result.html#variant.Err">Err in Rust.
486 def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]: 487 """Returns `True` if the contained value is `Ok` and `f()` returns True. 488 489 Example 490 ------- 491 ```py 492 value: Result[str, None] = Err(None) 493 494 assert value.is_ok_and(lambda inner: inner == "value") 495 # False 496 ``` 497 """ 498 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
501 def is_err(self) -> typing.Literal[True]: 502 """Returns `True` if the contained value is `Err`. 503 504 Example 505 ------- 506 ```py 507 value: Result[str, None] = Err(None) 508 509 assert value.is_err() == True 510 ``` 511 """ 512 return True
Returns True
if the contained value is Err
.
Example
value: Result[str, None] = Err(None)
assert value.is_err() == True
514 def is_err_and(self, f: F[E, bool]) -> bool: 515 """Returns `True` if the contained value is `Ok` and `f()` returns True.. 516 517 Example 518 ------- 519 ```py 520 value: Result[str, None] = Err(None) 521 522 assert value.is_err_and(lambda err: err is None) 523 # True 524 ``` 525 """ 526 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
532 def expect(self, msg: str) -> Never: 533 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 534 if it was `Err` with `message` passed to it. 535 536 Example 537 ------- 538 ```py 539 ok: Result[str, None] = Ok("owo") 540 ok.expect("err") # owo 541 542 err: Result[str, None] = Err(None) 543 err.expect("err") # RuntimeError("err") 544 ``` 545 """ 546 raise RuntimeError(msg) from None
548 def expect_err(self) -> E: 549 """Return the `Err` if it was `Err`, panicking otherwise. 550 551 552 Example 553 ------- 554 ```py 555 x: Result[str, None] = Ok("owo") 556 x.expect_err() # RuntimeError("Called expect_err on `Ok`) 557 558 x: Result[str, None] = Err(None) 559 x.expect_err() # None 560 ``` 561 """ 562 return self._inner
564 def unwrap(self) -> Never: 565 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 566 567 Example 568 ------- 569 ```py 570 ok: Result[str, None] = Ok("owo") 571 ok.unwrap() # owo 572 573 err: Result[str, None] = Err(None) 574 err.unwrap() # RuntimeError 575 ``` 576 """ 577 raise RuntimeError( 578 f"Called `unwrap()` on an `Err` variant: {self._inner!r}" 579 ) from None
581 def unwrap_or(self, default: T, /) -> T: 582 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 583 584 Example 585 ------- 586 ```py 587 ok: Result[str, None] = Ok("OwO") 588 ok.unwrap_or("uwu") # OwO 589 590 err: Result[str, None] = Err(None) 591 err.unwrap_or("uwu") # uwu 592 ``` 593 """ 594 return default
596 def unwrap_or_else(self, f: F[E, T]) -> T: 597 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 598 599 Example 600 ------- 601 ```py 602 ok: Result[int, str] = Ok(4) 603 ok.unwrap_or_else(lambda e: 0) # 4 604 605 err: Result[int, str] = Err("word") 606 err.unwrap_or_else(lambda e: len(e)) # 4 607 ``` 608 """ 609 return f(self._inner)
611 def unwrap_err(self) -> E: 612 """Return the contained `Err` value, Raising if it was `Ok`. 613 614 Example 615 ------- 616 ```py 617 ok: Result[str, None] = Ok("buh") 618 ok.unwrap_err() # RuntimeError 619 620 err: Result[str, None] = Err(None) 621 err.unwrap_err() == None 622 # True 623 ``` 624 """ 625 return self._inner
631 def inspect(self, f: F[T, typing.Any]) -> Self: 632 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 633 634 Example 635 ------- 636 ```py 637 def sink(value: str) -> None: 638 # do something with value 639 print("Called " + value) 640 641 x: Result[str, None] = Ok("ok") 642 x.inspect(sink) # "Called ok" 643 644 x: Result[str, str] = Err("err") 645 x.inspect(sink) # None 646 ``` 647 """ 648 return self
650 def inspect_err(self, f: F[E, typing.Any]) -> Self: 651 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 652 653 Example 654 ------- 655 ```py 656 def sink(value: str) -> None: 657 # do something with value 658 print("Called " + value) 659 660 x: Result[str, None] = Ok("ok") 661 x.inspect_err(sink) # None 662 663 x: Result[str, str] = Err("err") 664 x.inspect_err(sink) # Called err 665 ``` 666 """ 667 f(self._inner) 668 return self
670 def ok(self) -> Option[None]: 671 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 672 673 Example 674 ------- 675 ```py 676 value: Result[str, None] = Ok("buh") 677 value.ok().is_some() # True 678 679 value: Result[str, int] = Err(0) 680 value.ok().is_none() # True 681 ``` 682 """ 683 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
685 def err(self) -> Option[E]: 686 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 687 688 Example 689 ------- 690 ```py 691 value: Result[str, None] = Ok("buh") 692 value.err().is_none() # True 693 694 value: Result[str, int] = Err(0) 695 value.err().is_some() # True 696 ``` 697 """ 698 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
700 def map(self, f: F[E, U]) -> Self: 701 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 702 leaving `Err` untouched. 703 704 Example 705 ------- 706 ```py 707 ok: Result[str, int] = Ok("1") 708 ok.map(lambda c: int(c) + 1) # Ok(2) 709 710 err: Result[str, int] = Err(0) 711 err.map(str.upper) # Err(0) 712 ``` 713 """ 714 return self
716 def map_or(self, f: F[E, U], default: U, /) -> U: 717 """Returns the provided `default` if the contained value is `Err`, 718 719 Otherwise extracts the `Ok` value and maps it to `f()` 720 721 Example 722 ------- 723 ```py 724 x: Result[str, str] = Ok("foo") 725 assert x.map_or(lambda c: len(c), 42) == 3 726 727 x: Result[str, str] = Err("bar") 728 assert x.map_or(lambda c: len(c), 42) == 42 729 ``` 730 """ 731 return default
733 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 734 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 735 or function `f` to a contained Ok value. 736 737 Example 738 ------- 739 ```py 740 x: Result[str, str] = Ok("four") 741 assert x.map_or_else( 742 lambda ok: 2 * len(ok), 743 default=lambda err: len(err) 744 ) == 8 745 746 x: Result[str, str] = Err("bar") 747 assert x.map_or_else( 748 lambda c: 2 * len(c), 749 lambda err: len(err) 750 ) == 3 751 ``` 752 """ 753 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
755 def map_err(self, f: F[E, U]) -> Err[U]: 756 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 757 758 Example 759 ------- 760 ```py 761 x: Result[str, int] = Ok("blue") 762 x.map_err(lambda err: err + 1) # Ok("blue") 763 764 x: Result[str, int] = Err(5) 765 x.map_err(float) # Err(5.0) 766 ``` 767 """ 768 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)
774 def iter(self) -> _iter.Empty[Never]: 775 """An iterator over the possible contained value. 776 777 If `self` was `Ok`, then the iterator will yield `T`, otherwise yields nothing. 778 779 Example 780 ------- 781 ```py 782 c: Result[str, int] = Ok("blue") 783 c.iter().next() == Some("blue") 784 785 c: Result[str, int] = Err(0) 786 c.iter().next() == Some(None) 787 ``` 788 """ 789 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]): 73 """A contiguous growable alternative to builtin `list` with extra functionalities. 74 75 The layout of `Vec` is almost the same as `list`. 76 77 When initializing a vec, it will not build the underlying list until the first element gets pushed. 78 Which saves a little bit of memory. 79 80 Example 81 ------- 82 ```py 83 names = Vec() 84 names.push('foo') 85 names.push('bar') 86 87 print(names) # ['foo', 'bar'] 88 assert names.len() == 2 89 ``` 90 91 Constructing 92 ------------ 93 * `Vec()`: Create an unallocated vec, Which means the underlying list will be `None` until you start pushing into it 94 * `Vec(other_list)`: Create a vec which points to `other_list` 95 * `Vec((1, 2, 3))`: Create a vec with `[1, 2, 3]` pre-allocated 96 * `Vec.with_capacity(5)`: Create a vec that can hold up to 5 elements 97 * `from_args(1, 2, 3)`: Create a vec from arguments. This is not a classmethod 98 99 Iterating over `Vec` 100 ------------------- 101 There're two ways to iterate over a `Vec`. The first is to normally use `for` loop. 102 103 ```py 104 for i in names: 105 print(i) 106 107 # foo 108 # bar 109 ``` 110 111 The second is to use `Vec.iter`, which yields all items in this `Vec` from start to end. 112 Then the iterator gets exhausted as usual, See `sain.Iterator`. 113 114 ```py 115 iterator = names.iter() 116 for name in iterator.map(str.upper): 117 print(name) 118 119 # FOO 120 # BAR 121 122 # No more items, The actual vec is left unchanged. 123 assert iterator.next().is_none() 124 ``` 125 126 ## Comparison operators 127 A `Vec` may be compared with another `Vec` or a `list`, which is what this type is built on-top of. 128 any other type will return `False` 129 130 ```py 131 vec = Vec([1, 2, 3]) 132 assert vec == [1, 2, 3] # True 133 assert not vec == (1, 2, 3) 134 ``` 135 136 Zero-Copy 137 --------- 138 A vec that gets initialized from a `list` will *point* to it and doesn't copy it. 139 So any element that gets appended to the list will also get pushed into the vec 140 thats pointing to it. 141 142 ```py 143 cells: list[str] = [] 144 vec = Vec(cells) # This DOES NOT copy the `cells`. 145 146 cells.append("foo") 147 vec[0] == "foo" # True 148 ``` 149 150 The opposite of the above is to initialize the vec from either 151 an iterable or args, or copy the list. 152 153 ```py 154 from sain.collections import vec, Vec 155 156 # Creates a new vec and extend it with the elements. 157 from_args = vec.from_args("foo", "bar") 158 159 # inlined from another iterable. 160 from_iter = Vec(["foo", "bar"]) 161 162 # Copy the list into a vec. 163 vec = Vec(cells[:]) 164 cells.append("bar") 165 166 vec[1] # IndexError: "bar" doesn't exist in vec. 167 ``` 168 """ 169 170 __slots__ = ("_ptr", "_capacity") 171 172 @typing.overload 173 def __init__(self) -> None: ... 174 175 @typing.overload 176 def __init__(self, iterable: collections.Iterable[T]) -> None: ... 177 178 def __init__(self, iterable: collections.Iterable[T] | None = None) -> None: 179 # We won't allocate to build the list here. 180 # Instead, On first push or fist indexed set 181 # we allocate if it was None. 182 if isinstance(iterable, list): 183 # Calling `list()` on another list will copy it, So instead we just point to it. 184 self._ptr = iterable 185 elif isinstance(iterable, Vec): 186 self._ptr = iterable._ptr 187 # any other iterable that ain't a list needs to get copied into a new list. 188 else: 189 self._ptr: list[T] | None = list(iterable) if iterable else None 190 191 self._capacity: int | None = None 192 193 @classmethod 194 def with_capacity(cls, capacity: int) -> 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 Check out `Vec.push_within_capacity` as well. 199 200 Example 201 ------- 202 ```py 203 vec = Vec.with_capacity(3) 204 assert vec.len() == 0 and vec.capacity() >= 3 205 206 vec.push(1) 207 vec.push(2) 208 vec.push(3) 209 print(vec.len()) # 3 210 211 # This won't push. 212 vec.push(4) 213 ``` 214 """ 215 v = cls() 216 v._capacity = capacity 217 return v 218 219 def as_ref(self) -> collections.Collection[T]: 220 """Return an immutable view over this vector elements. 221 222 This will *copy* `self` elements into a new tuple. 223 224 Example 225 ------- 226 ```py 227 vec = Vec((1,2,3)) 228 assert vec.as_ref() == (1, 2, 3) 229 ``` 230 """ 231 return tuple(self) 232 233 def len(self) -> int: 234 """Return the number of elements in this vector. 235 236 Example 237 ------- 238 ```py 239 vec = Vec((1,2,3)) 240 241 assert vec.len() == 3 242 ``` 243 """ 244 return self.__len__() 245 246 def capacity(self) -> int: 247 """Return the capacity of this vector if set. 0 if not . 248 249 Example 250 ------- 251 ```py 252 vec_with_cap = Vec.with_capacity(3) 253 assert vec_with_cap.capacity().unwrap() == 3 254 255 vec = Vec([1, 2, 3]) 256 assert vec.capacity() == 0 257 ``` 258 """ 259 return 0 if self._capacity is None else self._capacity 260 261 def iter(self) -> _iter.TrustedIter[T]: 262 """Returns an iterator over this vector elements. 263 264 Example 265 ------- 266 ```py 267 vec = Vec([1 ,2, 3]) 268 for element in vec.iter().map(str): 269 print(element) 270 ``` 271 """ 272 return _iter.TrustedIter(self._ptr or ()) 273 274 def is_empty(self) -> bool: 275 """Returns true if the vector contains no elements.""" 276 return not self._ptr 277 278 def split_off(self, at: int) -> Vec[T]: 279 """Split the vector off at the specified position, returning a new 280 vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`. 281 282 if this vec is empty, `self` is returned unchanged. 283 284 Example 285 ------- 286 ```py 287 origin = Vec((1, 2, 3, 4)) 288 split = vec.split_off(2) 289 290 print(origin, split) # [1, 2], [3, 4] 291 ``` 292 293 Raises 294 ------ 295 `RuntimeError` 296 This method will raise if `at` > `len(self)` 297 """ 298 len_ = self.len() 299 if at > len_: 300 raise RuntimeError( 301 f"Index `at` ({at}) should be <= than len of vector ({len_}) " 302 ) from None 303 304 # Either the list is empty or uninit. 305 if not self._ptr: 306 return self 307 308 split = self[at:len_] # split the items into a new vec. 309 del self._ptr[at:len_] # remove the items from the original list. 310 return split 311 312 def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 313 """Split the first and rest elements of the vector, If empty, `None` is returned. 314 315 Example 316 ------- 317 ```py 318 vec = Vec([1, 2, 3]) 319 split = vec.split_first() 320 assert split == Some((1, [2, 3])) 321 322 vec: Vec[int] = Vec() 323 split = vec.split_first() 324 assert split.is_none() 325 ``` 326 """ 327 if not self._ptr: 328 return _option.NOTHING # pyright: ignore 329 330 # optimized to only one element in the vector. 331 if self.len() == 1: 332 return _option.Some((self[0], ())) 333 334 first, *rest = self._ptr 335 return _option.Some((first, rest)) 336 337 def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 338 """Split the last and rest elements of the vector, If empty, `None` is returned. 339 340 Example 341 ------- 342 ```py 343 vec = Vec([1, 2, 3]) 344 last, rest = vec.split_last().unwrap() 345 assert (last, rest) == [3, [1, 2]] 346 ``` 347 """ 348 if not self._ptr: 349 return _option.NOTHING # pyright: ignore 350 351 # optimized to only one element in the vector. 352 if self.len() == 1: 353 return _option.Some((self[0], ())) 354 355 last = self[-1] 356 return _option.Some((last, [*self[:-1]])) 357 358 def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]: 359 """Divide `self` into two at an index. 360 361 The first will contain all elements from `[0:mid]` excluding `mid` it self. 362 and the second will contain the remaninig elements. 363 364 if `mid` > `self.len()`, Then all elements will be moved to the left, 365 returning an empty vec in right. 366 367 Example 368 ------- 369 ```py 370 buffer = Vec((1, 2, 3, 4)) 371 left, right = buffer.split_at(0) 372 assert left == [] and right == [1, 2, 3, 4] 373 374 left, right = buffer.split_at(2) 375 assert left == [1, 2] and right == [2, 3] 376 ``` 377 378 The is roughly the implementation 379 ```py 380 self[0:mid], self[mid:] 381 ``` 382 """ 383 return self[0:mid], self[mid:] 384 385 def swap(self, a: int, b: int): 386 """Swap two elements in the vec. 387 388 if `a` equals to `b` then it's guaranteed that elements won't change value. 389 390 Example 391 ------- 392 ```py 393 buf = Vec([1, 2, 3, 4]) 394 buf.swap(0, 3) 395 assert buf == [4, 2, 3, 1] 396 ``` 397 398 Raises 399 ------ 400 IndexError 401 If the positions of `a` or `b` are out of index. 402 """ 403 if self[a] == self[b]: 404 return 405 406 self[a], self[b] = self[b], self[a] 407 408 def swap_unchecked(self, a: int, b: int): 409 """Swap two elements in the vec. without checking if `a` == `b`. 410 411 If you care about `a` and `b` equality, see `Vec.swap`. 412 413 Example 414 ------- 415 ```py 416 buf = Vec([1, 2, 3, 1]) 417 buf.swap_unchecked(0, 3) 418 assert buf == [1, 2, 3, 1] 419 ``` 420 421 Raises 422 ------ 423 IndexError 424 If the positions of `a` or `b` are out of index. 425 """ 426 self[a], self[b] = self[b], self[a] 427 428 def first(self) -> _option.Option[T]: 429 """Get the first element in this vec, returning `None` if there's none. 430 431 Example 432 ------- 433 ```py 434 vec = Vec((1,2,3)) 435 first = vec.first() 436 assert ~first == 1 437 ``` 438 """ 439 return self.get(0) 440 441 def last(self) -> _option.Option[T]: 442 """Get the last element in this vec, returning `None` if there's none. 443 444 Example 445 ------- 446 ```py 447 vec = Vec([1, 2, 3, 4]) 448 first = vec.last() 449 assert ~first == 4 450 ``` 451 """ 452 return self.get(-1) 453 454 def truncate(self, size: int) -> None: 455 """Shortens the vec, keeping the first `size` elements and dropping the rest. 456 457 Example 458 ------- 459 ```py 460 vec = Vec([1,2,3]) 461 vec.truncate(1) 462 assert vec == [1] 463 ``` 464 """ 465 if not self._ptr: 466 return 467 468 del self._ptr[size:] 469 470 def retain(self, f: collections.Callable[[T], bool]) -> None: 471 """Remove elements from this vec in-place while `f()` returns `True`. 472 473 In other words, filter this vector based on `f()`. 474 475 Example 476 ------- 477 ```py 478 vec = Vec([1, 2, 3]) 479 vec.retain(lambda elem: elem > 1) 480 481 assert vec == [2, 3] 482 ``` 483 """ 484 if not self._ptr: 485 return 486 487 for idx, e in enumerate(self._ptr): 488 if f(e): 489 del self._ptr[idx] 490 491 def swap_remove(self, item: T) -> T: 492 """Remove the first appearance of `item` from this vector and return it. 493 494 Raises 495 ------ 496 * `ValueError`: if `item` is not in this vector. 497 * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it. 498 499 Example 500 ------- 501 ```py 502 vec = Vec(('a', 'b', 'c')) 503 element = vec.remove('a') 504 assert vec == ['b', 'c'] and element == 'a' 505 ``` 506 """ 507 if self._ptr is None: 508 raise MemoryError("Vec is unallocated.") from None 509 510 return self._ptr.pop(self.index(item)) 511 512 def fill(self, value: T) -> None: 513 """Fill `self` with the given `value`. 514 515 Nothing happens if the vec is empty or unallocated. 516 517 Example 518 ```py 519 a = Vec([0, 1, 2, 3]) 520 a.fill(0) 521 assert a == [0, 0, 0, 0] 522 ``` 523 """ 524 if not self._ptr: 525 return 526 527 for n, _ in enumerate(self): 528 self[n] = value 529 530 def push(self, item: T) -> None: 531 """Push an element at the end of the vector. 532 533 Example 534 ------- 535 ```py 536 vec = Vec() 537 vec.push(1) 538 539 assert vec == [1] 540 ``` 541 """ 542 if self._capacity is not None: 543 self.push_within_capacity(item) 544 return 545 546 if self._ptr is None: 547 self._ptr = [] 548 549 self._ptr.append(item) 550 551 def push_within_capacity(self, x: T) -> Result[None, T]: 552 """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element. 553 554 Example 555 ------- 556 ```py 557 vec: Vec[int] = Vec.with_capacity(3) 558 for i in range(3): 559 match vec.push_within_capacity(i): 560 case Ok(_): 561 print("All good.") 562 case Err(elem): 563 print("Reached max cap :< cant push", elem) 564 ``` 565 566 Or you can also just call `Vec.push` and it will push if theres is sufficient capacity. 567 ```py 568 vec: Vec[int] = Vec.with_capacity(3) 569 570 vec.extend((1, 2, 3)) 571 vec.push(4) 572 573 assert vec.len() == 3 574 ``` 575 """ 576 if self._ptr is None: 577 self._ptr = [] 578 579 if self.len() == self._capacity: 580 return _result.Err(x) 581 582 self._ptr.append(x) 583 return _result.Ok(None) 584 585 def reserve(self, additional: int) -> None: 586 """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. 587 588 Example 589 ------- 590 ```py 591 vec = Vec.with_capacity(3) 592 is_vip = random.choice((True, False)) 593 594 for i in range(4): 595 match vec.push_within_capacity(i): 596 case Ok(_): 597 print("All good") 598 case Err(person): 599 # If the person is a VIP, then reserve for one more. 600 if is_vip: 601 vec.reserve(1) 602 continue 603 604 # is_vip was false. 605 print("Can't reserve for non-VIP members...", person) 606 break 607 ``` 608 """ 609 if self._capacity is not None: 610 self._capacity += additional 611 612 def shrink_to_fit(self) -> None: 613 """Shrink the capacity of this `Vec` to match its length. 614 615 If `self` is initialized with no capacity, This is a `NOP`. 616 617 Example 618 ------- 619 ```py 620 s = Vec([1, 2, 3, 4]) 621 622 s.reserve(100) 623 s.shrink_to_fit() 624 assert s.capacity() == 4 625 ``` 626 """ 627 if self._capacity is None: 628 return 629 630 # The capacity is never less than the length. 631 self._capacity = min(self.__len__(), self._capacity) 632 633 def shrink_to(self, min_capacity: int) -> None: 634 """Shrink the capacity of this `Vec` to a minimum specified capacity. 635 636 If `self` is initialized with no capacity or the current capacity is less than the lower limit, 637 This is a `NOP`. 638 639 Example 640 ------- 641 ```py 642 vec = Vec.with_capacity(10) 643 vec.extend([1, 2, 3]) 644 assert vec.capacity() >= 10 645 vec.shrink_to(4) 646 assert vec.capacity() >= 4 647 vec.shrink_to(0) 648 ``` 649 """ 650 if self._capacity is None or self._capacity <= min_capacity: 651 return 652 653 # Ensure the capacity is not reduced below the current length of the vector. 654 self._capacity = max(self.__len__(), min_capacity) 655 656 ########################## 657 # * Builtin Operations * 658 ########################## 659 660 # For people how are used to calling list.append 661 append = push 662 """An alias for `Vec.push` method.""" 663 664 def get(self, index: int) -> _option.Option[T]: 665 """Get the item at the given index, or `Some[None]` if its out of bounds. 666 667 Example 668 ------- 669 ```py 670 vec = Vec((1, 2, 3)) 671 vec.get(0) == Some(1) 672 vec.get(3) == Some(None) 673 ``` 674 """ 675 try: 676 return _option.Some(self[index]) 677 except IndexError: 678 return _option.NOTHING # pyright: ignore 679 680 def insert(self, index: int, value: T) -> None: 681 """Insert an element at the position `index`. 682 683 Example 684 -------- 685 ```py 686 vec = Vec((2, 3)) 687 vec.insert(0, 1) 688 assert vec == [1, 2, 3] 689 ``` 690 """ 691 self.__setitem__(index, value) 692 693 def pop(self, index: int = -1) -> _option.Option[T]: 694 """Removes the last element from a vector and returns it, or `sain.Some(None)` if it is empty. 695 696 Example 697 ------- 698 ```py 699 vec = Vec((1, 2, 3)) 700 assert vec.pop() == Some(3) 701 assert vec == [1, 2] 702 ``` 703 """ 704 if not self._ptr: 705 return _option.NOTHING # pyright: ignore 706 707 return _option.Some(self._ptr.pop(index)) 708 709 def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]: 710 """Removes the last element from a vector and returns it if `f` returns `True`, 711 or `None` if it is empty. 712 713 Example 714 ------- 715 ```py 716 vec = Vec((1, 2, 3)) 717 assert vec.pop_if(lambda num: num * 2 == 6) == Some(3) 718 assert vec == [1, 2] 719 ``` 720 """ 721 if not self._ptr: 722 return _option.NOTHING # pyright: ignore 723 724 if pred(self[-1]): 725 return self.pop() 726 727 return _option.NOTHING # pyright: ignore 728 729 def dedup(self) -> None: 730 """Removes duplicate elements from `self` in-place. 731 732 Example 733 ------- 734 ```py 735 vec = Vec([1, 2, 3, 3, 4, 1]) 736 vec.dedup() 737 assert vec == [1, 2, 3, 4] 738 """ 739 740 if not self._ptr: 741 return 742 743 seen: set[T] = set() 744 write_idx = 0 745 746 for read_idx, _ in enumerate(self._ptr): 747 if self._ptr[read_idx] not in seen: 748 seen.add(self._ptr[read_idx]) 749 self._ptr[write_idx] = self._ptr[read_idx] 750 write_idx += 1 751 752 del self._ptr[write_idx:] 753 754 def remove(self, item: T) -> None: 755 """Remove the first appearance of `item` from this vector. 756 757 Example 758 ------- 759 ```py 760 vec = Vec(('a', 'b', 'c')) 761 vec.remove('a') 762 assert vec == ['b', 'c'] 763 ``` 764 """ 765 if not self._ptr: 766 return 767 768 self._ptr.remove(item) 769 770 def extend(self, iterable: collections.Iterable[T]) -> None: 771 """Extend this vector from another iterable. 772 773 Example 774 ------- 775 ```py 776 vec = Vec((1, 2, 3)) 777 vec.extend((4, 5, 6)) 778 779 assert vec == [1, 2, 3, 4, 5, 6] 780 ``` 781 """ 782 if self._ptr is None: 783 self._ptr = [] 784 785 self._ptr.extend(iterable) 786 787 def copy(self) -> Vec[T]: 788 """Create a vector that copies all of its elements and place it into the new one. 789 790 Example 791 ------- 792 ```py 793 original = Vec((1,2,3)) 794 copy = original.copy() 795 copy.push(4) 796 797 print(original) # [1, 2, 3] 798 ``` 799 """ 800 return Vec(self._ptr[:]) if self._ptr else Vec() 801 802 def clear(self) -> None: 803 """Clear all elements of this vector. 804 805 Example 806 ------- 807 ```py 808 vec = Vec((1,2,3)) 809 vec.clear() 810 assert vec.len() == 0 811 ``` 812 """ 813 if not self._ptr: 814 return 815 816 self._ptr.clear() 817 818 def sort( 819 self, 820 *, 821 key: collections.Callable[[T], SupportsRichComparison] | None = None, 822 reverse: bool = False, 823 ) -> None: 824 """This method sorts the list in place, using only < comparisons between items. 825 826 Example 827 ------- 828 ```py 829 vec = Vec((2, 1, 3)) 830 vec.sort() 831 assert vec == [1, 2, 3] 832 ``` 833 """ 834 if not self._ptr: 835 return 836 837 # key can be `None` here just fine, idk why pyright is complaining. 838 self._ptr.sort(key=key, reverse=reverse) # pyright: ignore 839 840 def index( 841 self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize 842 ) -> int: 843 # << Official documentation >> 844 """Return zero-based index in the vec of the first item whose value is equal to `item`. 845 Raises a ValueError if there is no such item. 846 847 Example 848 ------- 849 ```py 850 vec = Vec((1, 2, 3)) 851 assert vec.index(2) == 1 852 ``` 853 """ 854 if self._ptr is None: 855 raise ValueError from None 856 857 return self._ptr.index(item, start, end) 858 859 def count(self, item: T) -> int: 860 """Return the number of occurrences of `item` in the vec. 861 862 `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found. 863 864 Example 865 -------- 866 ```py 867 vec = Vec((1, 2, 3, 3)) 868 assert vec.count(3) == 2 869 ``` 870 """ 871 if self._ptr is None: 872 return 0 873 874 return self._ptr.count(item) 875 876 def __len__(self) -> int: 877 return len(self._ptr) if self._ptr else 0 878 879 def __setitem__(self, index: int, value: T): 880 if not self._ptr: 881 raise IndexError from None 882 883 self._ptr[index] = value 884 885 @typing.overload 886 def __getitem__(self, index: slice) -> Vec[T]: ... 887 888 @typing.overload 889 def __getitem__(self, index: int) -> T: ... 890 891 def __getitem__(self, index: int | slice) -> T | Vec[T]: 892 if not self._ptr: 893 raise IndexError("Index out of range") 894 895 if isinstance(index, slice): 896 return Vec(self._ptr[index]) 897 898 return self._ptr[index] 899 900 def __delitem__(self, index: int) -> None: 901 if not self._ptr: 902 return 903 904 del self._ptr[index] 905 906 def __contains__(self, element: T) -> bool: 907 return element in self._ptr if self._ptr else False 908 909 def __iter__(self) -> collections.Iterator[T]: 910 if self._ptr is None: 911 return iter(()) 912 913 return self._ptr.__iter__() 914 915 def __repr__(self) -> str: 916 return _LIST_REPR if not self._ptr else repr(self._ptr) 917 918 def __eq__(self, other: object) -> bool: 919 if isinstance(other, Vec): 920 return self._ptr == other._ptr 921 922 elif isinstance(other, list): 923 return self._ptr == other 924 925 return NotImplemented 926 927 def __ne__(self, other: object) -> bool: 928 return not self.__eq__(other) 929 930 def __le__(self, other: list[T]) -> bool: 931 if not self._ptr: 932 return False 933 934 return self._ptr <= other 935 936 def __ge__(self, other: list[T]) -> bool: 937 if not self._ptr: 938 return False 939 940 return self._ptr >= other 941 942 def __lt__(self, other: list[T]) -> bool: 943 if not self._ptr: 944 return False 945 946 return self._ptr < other 947 948 def __gt__(self, other: list[T]) -> bool: 949 if not self._ptr: 950 return False 951 952 return self._ptr > other 953 954 def __bool__(self) -> bool: 955 return bool(self._ptr)
A contiguous growable alternative to builtin list
with extra functionalities.
The layout of Vec
is almost the same as list
.
When initializing a vec, it will not build the underlying list until the first element gets pushed. Which saves a little bit of memory.
Example
names = Vec()
names.push('foo')
names.push('bar')
print(names) # ['foo', 'bar']
assert names.len() == 2
Constructing
Vec()
: Create an unallocated vec, Which means the underlying list will beNone
until you start pushing into itVec(other_list)
: Create a vec which points toother_list
Vec((1, 2, 3))
: Create a vec with[1, 2, 3]
pre-allocatedVec.with_capacity(5)
: Create a vec that can hold up to 5 elementsfrom_args(1, 2, 3)
: Create a vec from arguments. This is not a classmethod
Iterating over Vec
There're two ways to iterate over a Vec
. The first is to normally use for
loop.
for i in names:
print(i)
# foo
# bar
The second is to use Vec.iter
, which yields all items in this Vec
from start to end.
Then the iterator gets exhausted as usual, See sain.Iterator
.
iterator = names.iter()
for name in iterator.map(str.upper):
print(name)
# FOO
# BAR
# No more items, The actual vec is left unchanged.
assert iterator.next().is_none()
Comparison operators
A Vec
may be compared with another Vec
or a list
, which is what this type is built on-top of.
any other type will return False
vec = Vec([1, 2, 3])
assert vec == [1, 2, 3] # True
assert not 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
thats pointing to it.
cells: list[str] = []
vec = Vec(cells) # This DOES NOT copy the `cells`.
cells.append("foo")
vec[0] == "foo" # True
The opposite of the above is to initialize the vec from either an iterable or args, or copy the list.
from sain.collections import vec, Vec
# Creates a new vec and extend it with the elements.
from_args = vec.from_args("foo", "bar")
# inlined from another iterable.
from_iter = Vec(["foo", "bar"])
# Copy the list into a vec.
vec = Vec(cells[:])
cells.append("bar")
vec[1] # IndexError: "bar" doesn't exist in vec.
Implementations
This class implements Vec in Rust.
178 def __init__(self, iterable: collections.Iterable[T] | None = None) -> None: 179 # We won't allocate to build the list here. 180 # Instead, On first push or fist indexed set 181 # we allocate if it was None. 182 if isinstance(iterable, list): 183 # Calling `list()` on another list will copy it, So instead we just point to it. 184 self._ptr = iterable 185 elif isinstance(iterable, Vec): 186 self._ptr = iterable._ptr 187 # any other iterable that ain't a list needs to get copied into a new list. 188 else: 189 self._ptr: list[T] | None = list(iterable) if iterable else None 190 191 self._capacity: int | None = None
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 Check out `Vec.push_within_capacity` as well. 199 200 Example 201 ------- 202 ```py 203 vec = Vec.with_capacity(3) 204 assert vec.len() == 0 and vec.capacity() >= 3 205 206 vec.push(1) 207 vec.push(2) 208 vec.push(3) 209 print(vec.len()) # 3 210 211 # This won't push. 212 vec.push(4) 213 ``` 214 """ 215 v = cls() 216 v._capacity = capacity 217 return v
Create a new Vec
with at least the specified capacity.
This vec will be able to hold capacity
elements without pushing further.
Check out Vec.push_within_capacity
as well.
Example
vec = Vec.with_capacity(3)
assert vec.len() == 0 and vec.capacity() >= 3
vec.push(1)
vec.push(2)
vec.push(3)
print(vec.len()) # 3
# This won't push.
vec.push(4)
219 def as_ref(self) -> collections.Collection[T]: 220 """Return an immutable view over this vector elements. 221 222 This will *copy* `self` elements into a new tuple. 223 224 Example 225 ------- 226 ```py 227 vec = Vec((1,2,3)) 228 assert vec.as_ref() == (1, 2, 3) 229 ``` 230 """ 231 return tuple(self)
Return an immutable view over this vector elements.
This will copy self
elements into a new tuple.
Example
vec = Vec((1,2,3))
assert vec.as_ref() == (1, 2, 3)
233 def len(self) -> int: 234 """Return the number of elements in this vector. 235 236 Example 237 ------- 238 ```py 239 vec = Vec((1,2,3)) 240 241 assert vec.len() == 3 242 ``` 243 """ 244 return self.__len__()
Return the number of elements in this vector.
Example
vec = Vec((1,2,3))
assert vec.len() == 3
246 def capacity(self) -> int: 247 """Return the capacity of this vector if set. 0 if not . 248 249 Example 250 ------- 251 ```py 252 vec_with_cap = Vec.with_capacity(3) 253 assert vec_with_cap.capacity().unwrap() == 3 254 255 vec = Vec([1, 2, 3]) 256 assert vec.capacity() == 0 257 ``` 258 """ 259 return 0 if self._capacity is None else self._capacity
Return the capacity of this vector if set. 0 if not .
Example
vec_with_cap = Vec.with_capacity(3)
assert vec_with_cap.capacity().unwrap() == 3
vec = Vec([1, 2, 3])
assert vec.capacity() == 0
261 def iter(self) -> _iter.TrustedIter[T]: 262 """Returns an iterator over this vector elements. 263 264 Example 265 ------- 266 ```py 267 vec = Vec([1 ,2, 3]) 268 for element in vec.iter().map(str): 269 print(element) 270 ``` 271 """ 272 return _iter.TrustedIter(self._ptr or ())
Returns an iterator over this vector elements.
Example
vec = Vec([1 ,2, 3])
for element in vec.iter().map(str):
print(element)
274 def is_empty(self) -> bool: 275 """Returns true if the vector contains no elements.""" 276 return not self._ptr
Returns true if the vector contains no elements.
278 def split_off(self, at: int) -> Vec[T]: 279 """Split the vector off at the specified position, returning a new 280 vec at the range of `[at : len]`, leaving `self` at `[at : vec_len]`. 281 282 if this vec is empty, `self` is returned unchanged. 283 284 Example 285 ------- 286 ```py 287 origin = Vec((1, 2, 3, 4)) 288 split = vec.split_off(2) 289 290 print(origin, split) # [1, 2], [3, 4] 291 ``` 292 293 Raises 294 ------ 295 `RuntimeError` 296 This method will raise if `at` > `len(self)` 297 """ 298 len_ = self.len() 299 if at > len_: 300 raise RuntimeError( 301 f"Index `at` ({at}) should be <= than len of vector ({len_}) " 302 ) from None 303 304 # Either the list is empty or uninit. 305 if not self._ptr: 306 return self 307 308 split = self[at:len_] # split the items into a new vec. 309 del self._ptr[at:len_] # remove the items from the original list. 310 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
RuntimeError
: This method will raise ifat
>len(self)
312 def split_first(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 313 """Split the first and rest elements of the vector, If empty, `None` is returned. 314 315 Example 316 ------- 317 ```py 318 vec = Vec([1, 2, 3]) 319 split = vec.split_first() 320 assert split == Some((1, [2, 3])) 321 322 vec: Vec[int] = Vec() 323 split = vec.split_first() 324 assert split.is_none() 325 ``` 326 """ 327 if not self._ptr: 328 return _option.NOTHING # pyright: ignore 329 330 # optimized to only one element in the vector. 331 if self.len() == 1: 332 return _option.Some((self[0], ())) 333 334 first, *rest = self._ptr 335 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()
337 def split_last(self) -> _option.Option[tuple[T, collections.Sequence[T]]]: 338 """Split the last and rest elements of the vector, If empty, `None` is returned. 339 340 Example 341 ------- 342 ```py 343 vec = Vec([1, 2, 3]) 344 last, rest = vec.split_last().unwrap() 345 assert (last, rest) == [3, [1, 2]] 346 ``` 347 """ 348 if not self._ptr: 349 return _option.NOTHING # pyright: ignore 350 351 # optimized to only one element in the vector. 352 if self.len() == 1: 353 return _option.Some((self[0], ())) 354 355 last = self[-1] 356 return _option.Some((last, [*self[:-1]]))
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]]
358 def split_at(self, mid: int) -> tuple[Vec[T], Vec[T]]: 359 """Divide `self` into two at an index. 360 361 The first will contain all elements from `[0:mid]` excluding `mid` it self. 362 and the second will contain the remaninig elements. 363 364 if `mid` > `self.len()`, Then all elements will be moved to the left, 365 returning an empty vec in right. 366 367 Example 368 ------- 369 ```py 370 buffer = Vec((1, 2, 3, 4)) 371 left, right = buffer.split_at(0) 372 assert left == [] and right == [1, 2, 3, 4] 373 374 left, right = buffer.split_at(2) 375 assert left == [1, 2] and right == [2, 3] 376 ``` 377 378 The is roughly the implementation 379 ```py 380 self[0:mid], self[mid:] 381 ``` 382 """ 383 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 remaninig 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
self[0:mid], self[mid:]
385 def swap(self, a: int, b: int): 386 """Swap two elements in the vec. 387 388 if `a` equals to `b` then it's guaranteed that elements won't change value. 389 390 Example 391 ------- 392 ```py 393 buf = Vec([1, 2, 3, 4]) 394 buf.swap(0, 3) 395 assert buf == [4, 2, 3, 1] 396 ``` 397 398 Raises 399 ------ 400 IndexError 401 If the positions of `a` or `b` are out of index. 402 """ 403 if self[a] == self[b]: 404 return 405 406 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.
408 def swap_unchecked(self, a: int, b: int): 409 """Swap two elements in the vec. without checking if `a` == `b`. 410 411 If you care about `a` and `b` equality, see `Vec.swap`. 412 413 Example 414 ------- 415 ```py 416 buf = Vec([1, 2, 3, 1]) 417 buf.swap_unchecked(0, 3) 418 assert buf == [1, 2, 3, 1] 419 ``` 420 421 Raises 422 ------ 423 IndexError 424 If the positions of `a` or `b` are out of index. 425 """ 426 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.
428 def first(self) -> _option.Option[T]: 429 """Get the first element in this vec, returning `None` if there's none. 430 431 Example 432 ------- 433 ```py 434 vec = Vec((1,2,3)) 435 first = vec.first() 436 assert ~first == 1 437 ``` 438 """ 439 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
441 def last(self) -> _option.Option[T]: 442 """Get the last element in this vec, returning `None` if there's none. 443 444 Example 445 ------- 446 ```py 447 vec = Vec([1, 2, 3, 4]) 448 first = vec.last() 449 assert ~first == 4 450 ``` 451 """ 452 return self.get(-1)
Get the last element in this vec, returning None
if there's none.
Example
vec = Vec([1, 2, 3, 4])
first = vec.last()
assert ~first == 4
454 def truncate(self, size: int) -> None: 455 """Shortens the vec, keeping the first `size` elements and dropping the rest. 456 457 Example 458 ------- 459 ```py 460 vec = Vec([1,2,3]) 461 vec.truncate(1) 462 assert vec == [1] 463 ``` 464 """ 465 if not self._ptr: 466 return 467 468 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]
470 def retain(self, f: collections.Callable[[T], bool]) -> None: 471 """Remove elements from this vec in-place while `f()` returns `True`. 472 473 In other words, filter this vector based on `f()`. 474 475 Example 476 ------- 477 ```py 478 vec = Vec([1, 2, 3]) 479 vec.retain(lambda elem: elem > 1) 480 481 assert vec == [2, 3] 482 ``` 483 """ 484 if not self._ptr: 485 return 486 487 for idx, e in enumerate(self._ptr): 488 if f(e): 489 del self._ptr[idx]
Remove elements from this vec in-place while f()
returns True
.
In other words, filter this vector based on f()
.
Example
vec = Vec([1, 2, 3])
vec.retain(lambda elem: elem > 1)
assert vec == [2, 3]
491 def swap_remove(self, item: T) -> T: 492 """Remove the first appearance of `item` from this vector and return it. 493 494 Raises 495 ------ 496 * `ValueError`: if `item` is not in this vector. 497 * `MemoryError`: if this vector hasn't allocated, Aka nothing has been pushed to it. 498 499 Example 500 ------- 501 ```py 502 vec = Vec(('a', 'b', 'c')) 503 element = vec.remove('a') 504 assert vec == ['b', 'c'] and element == 'a' 505 ``` 506 """ 507 if self._ptr is None: 508 raise MemoryError("Vec is unallocated.") from None 509 510 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.remove('a')
assert vec == ['b', 'c'] and element == 'a'
512 def fill(self, value: T) -> None: 513 """Fill `self` with the given `value`. 514 515 Nothing happens if the vec is empty or unallocated. 516 517 Example 518 ```py 519 a = Vec([0, 1, 2, 3]) 520 a.fill(0) 521 assert a == [0, 0, 0, 0] 522 ``` 523 """ 524 if not self._ptr: 525 return 526 527 for n, _ in enumerate(self): 528 self[n] = value
Fill self
with the given value
.
Nothing happens if the vec is empty or unallocated.
Example
a = Vec([0, 1, 2, 3])
a.fill(0)
assert a == [0, 0, 0, 0]
530 def push(self, item: T) -> None: 531 """Push an element at the end of the vector. 532 533 Example 534 ------- 535 ```py 536 vec = Vec() 537 vec.push(1) 538 539 assert vec == [1] 540 ``` 541 """ 542 if self._capacity is not None: 543 self.push_within_capacity(item) 544 return 545 546 if self._ptr is None: 547 self._ptr = [] 548 549 self._ptr.append(item)
Push an element at the end of the vector.
Example
vec = Vec()
vec.push(1)
assert vec == [1]
551 def push_within_capacity(self, x: T) -> Result[None, T]: 552 """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element. 553 554 Example 555 ------- 556 ```py 557 vec: Vec[int] = Vec.with_capacity(3) 558 for i in range(3): 559 match vec.push_within_capacity(i): 560 case Ok(_): 561 print("All good.") 562 case Err(elem): 563 print("Reached max cap :< cant push", elem) 564 ``` 565 566 Or you can also just call `Vec.push` and it will push if theres is sufficient capacity. 567 ```py 568 vec: Vec[int] = Vec.with_capacity(3) 569 570 vec.extend((1, 2, 3)) 571 vec.push(4) 572 573 assert vec.len() == 3 574 ``` 575 """ 576 if self._ptr is None: 577 self._ptr = [] 578 579 if self.len() == self._capacity: 580 return _result.Err(x) 581 582 self._ptr.append(x) 583 return _result.Ok(None)
Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
Example
vec: Vec[int] = Vec.with_capacity(3)
for i in range(3):
match vec.push_within_capacity(i):
case Ok(_):
print("All good.")
case Err(elem):
print("Reached max cap :< cant push", elem)
Or you can also just call Vec.push
and it will push if theres is sufficient capacity.
vec: Vec[int] = Vec.with_capacity(3)
vec.extend((1, 2, 3))
vec.push(4)
assert vec.len() == 3
585 def reserve(self, additional: int) -> None: 586 """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. 587 588 Example 589 ------- 590 ```py 591 vec = Vec.with_capacity(3) 592 is_vip = random.choice((True, False)) 593 594 for i in range(4): 595 match vec.push_within_capacity(i): 596 case Ok(_): 597 print("All good") 598 case Err(person): 599 # If the person is a VIP, then reserve for one more. 600 if is_vip: 601 vec.reserve(1) 602 continue 603 604 # is_vip was false. 605 print("Can't reserve for non-VIP members...", person) 606 break 607 ``` 608 """ 609 if self._capacity is not None: 610 self._capacity += additional
Reserves capacity for at least additional more elements to be inserted in the given Vec
Example
vec = Vec.with_capacity(3)
is_vip = random.choice((True, False))
for i in range(4):
match vec.push_within_capacity(i):
case Ok(_):
print("All good")
case Err(person):
# If the person is a VIP, then reserve for one more.
if is_vip:
vec.reserve(1)
continue
# is_vip was false.
print("Can't reserve for non-VIP members...", person)
break
612 def shrink_to_fit(self) -> None: 613 """Shrink the capacity of this `Vec` to match its length. 614 615 If `self` is initialized with no capacity, This is a `NOP`. 616 617 Example 618 ------- 619 ```py 620 s = Vec([1, 2, 3, 4]) 621 622 s.reserve(100) 623 s.shrink_to_fit() 624 assert s.capacity() == 4 625 ``` 626 """ 627 if self._capacity is None: 628 return 629 630 # The capacity is never less than the length. 631 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
633 def shrink_to(self, min_capacity: int) -> None: 634 """Shrink the capacity of this `Vec` to a minimum specified capacity. 635 636 If `self` is initialized with no capacity or the current capacity is less than the lower limit, 637 This is a `NOP`. 638 639 Example 640 ------- 641 ```py 642 vec = Vec.with_capacity(10) 643 vec.extend([1, 2, 3]) 644 assert vec.capacity() >= 10 645 vec.shrink_to(4) 646 assert vec.capacity() >= 4 647 vec.shrink_to(0) 648 ``` 649 """ 650 if self._capacity is None or self._capacity <= min_capacity: 651 return 652 653 # Ensure the capacity is not reduced below the current length of the vector. 654 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)
530 def push(self, item: T) -> None: 531 """Push an element at the end of the vector. 532 533 Example 534 ------- 535 ```py 536 vec = Vec() 537 vec.push(1) 538 539 assert vec == [1] 540 ``` 541 """ 542 if self._capacity is not None: 543 self.push_within_capacity(item) 544 return 545 546 if self._ptr is None: 547 self._ptr = [] 548 549 self._ptr.append(item)
An alias for Vec.push
method.
664 def get(self, index: int) -> _option.Option[T]: 665 """Get the item at the given index, or `Some[None]` if its out of bounds. 666 667 Example 668 ------- 669 ```py 670 vec = Vec((1, 2, 3)) 671 vec.get(0) == Some(1) 672 vec.get(3) == Some(None) 673 ``` 674 """ 675 try: 676 return _option.Some(self[index]) 677 except IndexError: 678 return _option.NOTHING # pyright: ignore
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)
680 def insert(self, index: int, value: T) -> None: 681 """Insert an element at the position `index`. 682 683 Example 684 -------- 685 ```py 686 vec = Vec((2, 3)) 687 vec.insert(0, 1) 688 assert vec == [1, 2, 3] 689 ``` 690 """ 691 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]
693 def pop(self, index: int = -1) -> _option.Option[T]: 694 """Removes the last element from a vector and returns it, or `sain.Some(None)` if it is empty. 695 696 Example 697 ------- 698 ```py 699 vec = Vec((1, 2, 3)) 700 assert vec.pop() == Some(3) 701 assert vec == [1, 2] 702 ``` 703 """ 704 if not self._ptr: 705 return _option.NOTHING # pyright: ignore 706 707 return _option.Some(self._ptr.pop(index))
Removes the last element from a vector and returns it, or sain.Some(None)
if it is empty.
Example
vec = Vec((1, 2, 3))
assert vec.pop() == Some(3)
assert vec == [1, 2]
709 def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]: 710 """Removes the last element from a vector and returns it if `f` returns `True`, 711 or `None` if it is empty. 712 713 Example 714 ------- 715 ```py 716 vec = Vec((1, 2, 3)) 717 assert vec.pop_if(lambda num: num * 2 == 6) == Some(3) 718 assert vec == [1, 2] 719 ``` 720 """ 721 if not self._ptr: 722 return _option.NOTHING # pyright: ignore 723 724 if pred(self[-1]): 725 return self.pop() 726 727 return _option.NOTHING # pyright: ignore
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]
729 def dedup(self) -> None: 730 """Removes duplicate elements from `self` in-place. 731 732 Example 733 ------- 734 ```py 735 vec = Vec([1, 2, 3, 3, 4, 1]) 736 vec.dedup() 737 assert vec == [1, 2, 3, 4] 738 """ 739 740 if not self._ptr: 741 return 742 743 seen: set[T] = set() 744 write_idx = 0 745 746 for read_idx, _ in enumerate(self._ptr): 747 if self._ptr[read_idx] not in seen: 748 seen.add(self._ptr[read_idx]) 749 self._ptr[write_idx] = self._ptr[read_idx] 750 write_idx += 1 751 752 del self._ptr[write_idx:]
Removes duplicate elements from self
in-place.
Example
```py vec = Vec([1, 2, 3, 3, 4, 1]) vec.dedup() assert vec == [1, 2, 3, 4]
754 def remove(self, item: T) -> None: 755 """Remove the first appearance of `item` from this vector. 756 757 Example 758 ------- 759 ```py 760 vec = Vec(('a', 'b', 'c')) 761 vec.remove('a') 762 assert vec == ['b', 'c'] 763 ``` 764 """ 765 if not self._ptr: 766 return 767 768 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']
770 def extend(self, iterable: collections.Iterable[T]) -> None: 771 """Extend this vector from another iterable. 772 773 Example 774 ------- 775 ```py 776 vec = Vec((1, 2, 3)) 777 vec.extend((4, 5, 6)) 778 779 assert vec == [1, 2, 3, 4, 5, 6] 780 ``` 781 """ 782 if self._ptr is None: 783 self._ptr = [] 784 785 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]
787 def copy(self) -> Vec[T]: 788 """Create a vector that copies all of its elements and place it into the new one. 789 790 Example 791 ------- 792 ```py 793 original = Vec((1,2,3)) 794 copy = original.copy() 795 copy.push(4) 796 797 print(original) # [1, 2, 3] 798 ``` 799 """ 800 return Vec(self._ptr[:]) if self._ptr else Vec()
Create a vector that copies all of its elements and place it into the new one.
Example
original = Vec((1,2,3))
copy = original.copy()
copy.push(4)
print(original) # [1, 2, 3]
802 def clear(self) -> None: 803 """Clear all elements of this vector. 804 805 Example 806 ------- 807 ```py 808 vec = Vec((1,2,3)) 809 vec.clear() 810 assert vec.len() == 0 811 ``` 812 """ 813 if not self._ptr: 814 return 815 816 self._ptr.clear()
Clear all elements of this vector.
Example
vec = Vec((1,2,3))
vec.clear()
assert vec.len() == 0
818 def sort( 819 self, 820 *, 821 key: collections.Callable[[T], SupportsRichComparison] | None = None, 822 reverse: bool = False, 823 ) -> None: 824 """This method sorts the list in place, using only < comparisons between items. 825 826 Example 827 ------- 828 ```py 829 vec = Vec((2, 1, 3)) 830 vec.sort() 831 assert vec == [1, 2, 3] 832 ``` 833 """ 834 if not self._ptr: 835 return 836 837 # key can be `None` here just fine, idk why pyright is complaining. 838 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]
840 def index( 841 self, item: T, start: typing.SupportsIndex = 0, end: int = _sys.maxsize 842 ) -> int: 843 # << Official documentation >> 844 """Return zero-based index in the vec of the first item whose value is equal to `item`. 845 Raises a ValueError if there is no such item. 846 847 Example 848 ------- 849 ```py 850 vec = Vec((1, 2, 3)) 851 assert vec.index(2) == 1 852 ``` 853 """ 854 if self._ptr is None: 855 raise ValueError from None 856 857 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
859 def count(self, item: T) -> int: 860 """Return the number of occurrences of `item` in the vec. 861 862 `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found. 863 864 Example 865 -------- 866 ```py 867 vec = Vec((1, 2, 3, 3)) 868 assert vec.count(3) == 2 869 ``` 870 """ 871 if self._ptr is None: 872 return 0 873 874 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
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 from dataclasses import dataclass 80 81 from sain import Error, Option, Some 82 from sain import Result, Ok, Err 83 84 # Base error. 85 class HTTPError(Error): ... 86 87 @dataclass 88 class NotFound(HTTPError): 89 message = "The response returned [404]: not found." 90 http_status = 404 91 response: requests.Response 92 93 def description(self) -> str: 94 return "Couldn't find what you're looking for " + self.response.url 95 96 # It is not necessary to define this method, 97 # it just gives more context to the user handling this error. 98 def source(self) -> Option[type[HTTPError]]: 99 return Some(HTTPError) 100 101 @dataclass 102 class UserNotFound(NotFound): 103 user_id: int 104 105 def __post_init__(self) -> None: 106 request = self.response.request 107 self.message = f"User {self.user_id} fetched from {request.path_url} was not found." 108 109 # It is not necessary to define this method, 110 # it just gives more context to the user handling this error. 111 def source(self) -> Option[type[NotFound]]: 112 return Some(NotFound) 113 114 def description(self) -> str: 115 return f"Couldn't find the resource: {self.response.raw}." 116 117 # A simple request that handles [404] responses. 118 def request( 119 url: str, 120 resourceful: bool = False, 121 uid: int 122 ) -> Result[requests.Response, HTTPError]: 123 response = requests.get( 124 url, 125 json={"resourceful": True, "user_id": uid} 126 if resourceful else None 127 ) 128 129 if response.status_code == 404: 130 if resourceful: 131 return Err(UserNotFound(response, uid)) 132 return Err(NotFound(response)) 133 134 return Ok(response) 135 136 # Execute the request 137 match request("some-url.com', True, uid=0): 138 case Ok(response): 139 # Deal with the response 140 ... 141 case Err(why): 142 # Deal with the error. 143 print(why.message) 144 ``` 145 """ 146 147 __slots__ = ("message",) 148 149 def __init__(self, message: str = "") -> None: 150 self.message = message 151 """A basic error message.""" 152 153 def source(self) -> Option[type[Error]]: 154 """The source of this error, if any.""" 155 return _option.NOTHING # pyright: ignore 156 157 def description(self) -> str: 158 """Context for this error.""" 159 return "" 160 161 def to_string(self) -> str: 162 return self.__repr__() 163 164 def __repr__(self) -> str: 165 source = None if (src := self.source()).is_none() else src 166 return ( 167 f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})' 168 ) 169 170 def __str__(self) -> str: 171 return self.message 172 173 # An error is always falsy. 174 def __bool__(self) -> typing.Literal[False]: 175 return False
Error
is an interface usually used for values that returns sain.Result[T, E]
where E
is an implementation of this interface.
Example
import requests
from dataclasses import dataclass
from sain import Error, Option, Some
from sain import Result, Ok, Err
# Base error.
class HTTPError(Error): ...
@dataclass
class NotFound(HTTPError):
message = "The response returned [404]: not found."
http_status = 404
response: requests.Response
def description(self) -> str:
return "Couldn't find what you're looking for " + self.response.url
# It is not necessary to define this method,
# it just gives more context to the user handling this error.
def source(self) -> Option[type[HTTPError]]:
return Some(HTTPError)
@dataclass
class UserNotFound(NotFound):
user_id: int
def __post_init__(self) -> None:
request = self.response.request
self.message = f"User {self.user_id} fetched from {request.path_url} was not found."
# It is not necessary to define this method,
# it just gives more context to the user handling this error.
def source(self) -> Option[type[NotFound]]:
return Some(NotFound)
def description(self) -> str:
return f"Couldn't find the resource: {self.response.raw}."
# A simple request that handles [404] responses.
def request(
url: str,
resourceful: bool = False,
uid: int
) -> Result[requests.Response, HTTPError]:
response = requests.get(
url,
json={"resourceful": True, "user_id": uid}
if resourceful else None
)
if response.status_code == 404:
if resourceful:
return Err(UserNotFound(response, uid))
return Err(NotFound(response))
return Ok(response)
# Execute the request
match request("some-url.com', True, uid=0):
case Ok(response):
# Deal with the response
...
case Err(why):
# Deal with the error.
print(why.message)
Implementations
This class implements Error in Rust.
153 def source(self) -> Option[type[Error]]: 154 """The source of this error, if any.""" 155 return _option.NOTHING # pyright: ignore
The source of this error, if any.
58@typing.final 59class Box(typing.Generic[T]): 60 """The box object for expiring data. not thread-safe. 61 62 A box is an object that contains a value of type `T` which expires it after the given amount of time, 63 The box won't start expiring the data until its first access with `Box.get` method. 64 65 Example 66 ------- 67 ```py 68 # Initializing a box doesn't mean it started expiring. instead, 69 # getting the value the first time will start the process. 70 cache: dict[str, Box[int]] = {"sora": Box(999, timedelta(seconds=5)} 71 72 # first start expiring here. 73 cache["sora"].get().unwrap() 74 time.sleep(6) 75 assert cache["sora"].has_expired() 76 ``` 77 """ 78 79 __slots__ = ("_inner", "_expire_in", "_on_expire", "_mono") 80 81 def __init__(self, value: T, expire_in: int | float | datetime.timedelta) -> None: 82 if isinstance(expire_in, datetime.timedelta): 83 expire_in = expire_in.total_seconds() 84 else: 85 expire_in = float(expire_in) 86 87 if expire_in <= 0: 88 raise ValueError("expire_in must be more than 0 seconds.") 89 90 # We set the last call on the first access to the value. 91 self._mono: float | None = None 92 self._inner: Option[T] = option.Some(value) 93 self._on_expire: collections.Callable[[T], typing.Any] | None = None 94 self._expire_in = expire_in 95 96 @property 97 def has_expired(self) -> bool: 98 """Returns True if the value has expired.""" 99 # return self._mono is not None and not self._expire_in <= ( 100 # time.monotonic() - self._mono 101 # ) 102 return self._mono is not None and ( 103 not self._mono or self._expire_in <= (time.monotonic() - self._mono) 104 ) 105 106 def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self: 107 """Set a callback that will be invoked when this value gets expired. 108 109 Both async and sync callbacks are supported. 110 111 Example 112 ------- 113 ```py 114 async def sink(message: str) -> None: 115 await client.create_message(message) 116 print("Sinked", message) 117 118 box = Box("bluh", 5).on_expire(sink) 119 120 while box.get().is_some(): 121 time.sleep(5) 122 ``` 123 First `.get` call on an expired box, the `sink` callback will be invoked, 124 also the inner value will be set to `Some(None)`. 125 126 After 5 seconds. 127 ```py 128 assert box.get() == Some("bluh") # This last call invokes the callback. 129 # Sinked bluh 130 assert box.get().is_none() 131 ``` 132 """ 133 self._on_expire = callback 134 return self 135 136 def remaining(self) -> float: 137 """Returns when this box will expire in seconds. 138 139 Example 140 -------- 141 ```py 142 jogo = Box("jogo", 3) 143 assert jogo.get().unwrap() == "jogo" 144 145 time.sleep(1) 146 assert jogo.remaining() == 2 147 ``` 148 """ 149 if not self._mono: 150 return 0.0 151 152 return math.floor( 153 (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99 154 ) 155 156 def get(self) -> Option[T]: 157 """Get the contained value if it was not expired, otherwise `Some(None)` is returned. 158 159 Example 160 ------- 161 ```py 162 pizza = Box("pizza", timedelta(days=1)) 163 164 while not pizza.get().is_none(): 165 # Do stuff with the value while its not expired. 166 167 # After 1 day. 168 assert pizza.get().is_none() 169 ``` 170 """ 171 if self.has_expired: 172 if self._on_expire is not None: 173 try: 174 if asyncio.iscoroutinefunction(self._on_expire): 175 futures.loop().run_until_complete( 176 self._on_expire(self._inner.unwrap_unchecked()) 177 ) 178 else: 179 self._on_expire(self._inner.unwrap_unchecked()) 180 finally: 181 self._on_expire = None 182 183 self._inner.take() 184 self._mono = None 185 # SAFETY: The value is expired, therefore we always return None. 186 return option.NOTHING # pyright: ignore 187 188 if self._mono is None: 189 self._mono = time.monotonic() 190 191 return self._inner 192 193 def __repr__(self) -> str: 194 return f"Box(value: {self._inner}, expired: {self.has_expired})" 195 196 __str__ = __repr__ 197 198 def __eq__(self, value: object, /) -> bool: 199 if not isinstance(value, Box): 200 return NotImplemented 201 202 return ( 203 self._inner == value._inner 204 and not self.has_expired 205 and not value.has_expired 206 ) 207 208 def __ne__(self, value: object, /) -> bool: 209 return not self.__eq__(value) 210 211 def __hash__(self) -> int: 212 return hash(self._inner) 213 214 def __bool__(self) -> bool: 215 return not self.has_expired
The box object for expiring data. not thread-safe.
A box is an object that contains a value of type T
which expires it after the given amount of time,
The box won't start expiring the data until its first access with Box.get
method.
Example
# Initializing a box doesn't mean it started expiring. instead,
# getting the value the first time will start the process.
cache: dict[str, Box[int]] = {"sora": Box(999, timedelta(seconds=5)}
# first start expiring here.
cache["sora"].get().unwrap()
time.sleep(6)
assert cache["sora"].has_expired()
81 def __init__(self, value: T, expire_in: int | float | datetime.timedelta) -> None: 82 if isinstance(expire_in, datetime.timedelta): 83 expire_in = expire_in.total_seconds() 84 else: 85 expire_in = float(expire_in) 86 87 if expire_in <= 0: 88 raise ValueError("expire_in must be more than 0 seconds.") 89 90 # We set the last call on the first access to the value. 91 self._mono: float | None = None 92 self._inner: Option[T] = option.Some(value) 93 self._on_expire: collections.Callable[[T], typing.Any] | None = None 94 self._expire_in = expire_in
96 @property 97 def has_expired(self) -> bool: 98 """Returns True if the value has expired.""" 99 # return self._mono is not None and not self._expire_in <= ( 100 # time.monotonic() - self._mono 101 # ) 102 return self._mono is not None and ( 103 not self._mono or self._expire_in <= (time.monotonic() - self._mono) 104 )
Returns True if the value has expired.
106 def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self: 107 """Set a callback that will be invoked when this value gets expired. 108 109 Both async and sync callbacks are supported. 110 111 Example 112 ------- 113 ```py 114 async def sink(message: str) -> None: 115 await client.create_message(message) 116 print("Sinked", message) 117 118 box = Box("bluh", 5).on_expire(sink) 119 120 while box.get().is_some(): 121 time.sleep(5) 122 ``` 123 First `.get` call on an expired box, the `sink` callback will be invoked, 124 also the inner value will be set to `Some(None)`. 125 126 After 5 seconds. 127 ```py 128 assert box.get() == Some("bluh") # This last call invokes the callback. 129 # Sinked bluh 130 assert box.get().is_none() 131 ``` 132 """ 133 self._on_expire = callback 134 return self
Set a callback that will be invoked when this value gets expired.
Both async and sync callbacks are supported.
Example
async def sink(message: str) -> None:
await client.create_message(message)
print("Sinked", message)
box = Box("bluh", 5).on_expire(sink)
while box.get().is_some():
time.sleep(5)
First .get
call on an expired box, the sink
callback will be invoked,
also the inner value will be set to Some(None)
.
After 5 seconds.
assert box.get() == Some("bluh") # This last call invokes the callback.
# Sinked bluh
assert box.get().is_none()
136 def remaining(self) -> float: 137 """Returns when this box will expire in seconds. 138 139 Example 140 -------- 141 ```py 142 jogo = Box("jogo", 3) 143 assert jogo.get().unwrap() == "jogo" 144 145 time.sleep(1) 146 assert jogo.remaining() == 2 147 ``` 148 """ 149 if not self._mono: 150 return 0.0 151 152 return math.floor( 153 (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99 154 )
Returns when this box will expire in seconds.
Example
jogo = Box("jogo", 3)
assert jogo.get().unwrap() == "jogo"
time.sleep(1)
assert jogo.remaining() == 2
156 def get(self) -> Option[T]: 157 """Get the contained value if it was not expired, otherwise `Some(None)` is returned. 158 159 Example 160 ------- 161 ```py 162 pizza = Box("pizza", timedelta(days=1)) 163 164 while not pizza.get().is_none(): 165 # Do stuff with the value while its not expired. 166 167 # After 1 day. 168 assert pizza.get().is_none() 169 ``` 170 """ 171 if self.has_expired: 172 if self._on_expire is not None: 173 try: 174 if asyncio.iscoroutinefunction(self._on_expire): 175 futures.loop().run_until_complete( 176 self._on_expire(self._inner.unwrap_unchecked()) 177 ) 178 else: 179 self._on_expire(self._inner.unwrap_unchecked()) 180 finally: 181 self._on_expire = None 182 183 self._inner.take() 184 self._mono = None 185 # SAFETY: The value is expired, therefore we always return None. 186 return option.NOTHING # pyright: ignore 187 188 if self._mono is None: 189 self._mono = time.monotonic() 190 191 return self._inner
Get the contained value if it was not expired, otherwise Some(None)
is returned.
Example
pizza = Box("pizza", timedelta(days=1))
while not pizza.get().is_none():
# Do stuff with the value while its not expired.
# After 1 day.
assert pizza.get().is_none()
117@rustc_diagnostic_item("From") 118@typing.runtime_checkable 119class From(typing.Protocol[T_co]): 120 """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into. 121 122 As the Rust documentation says, One should always prefer implementing From over Into 123 because implementing From automatically provides one with an implementation of Into. 124 125 But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types 126 that impl `From<T>`. 127 128 So for the sake of simplicity, You should implement whichever interface you want deal with, 129 Or simply, implement both as the same time. 130 131 Example 132 ------- 133 ```py 134 @dataclass 135 class Id(From[str]): 136 value: int 137 138 @classmethod 139 def from_t(cls, value: str) -> Self: 140 return cls(value=int(value)) 141 142 ``` 143 """ 144 145 __slots__ = () 146 147 @classmethod 148 def from_t(cls, value: T_co) -> Self: 149 """Perform the conversion.""" 150 raise NotImplementedError
Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
As the Rust documentation says, One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into.
But there's no such thing in Python, as it's impossible to auto-impl Into<T>
for all types
that impl From<T>
.
So for the sake of simplicity, You should implement whichever interface you want deal with, Or simply, implement both as the same time.
Example
@dataclass
class Id(From[str]):
value: int
@classmethod
def from_t(cls, value: str) -> Self:
return cls(value=int(value))
Implementations
This class implements From in Rust.
1941def _no_init_or_replace_init(self, *args, **kwargs): 1942 cls = type(self) 1943 1944 if cls._is_protocol: 1945 raise TypeError('Protocols cannot be instantiated') 1946 1947 # Already using a custom `__init__`. No need to calculate correct 1948 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1949 if cls.__init__ is not _no_init_or_replace_init: 1950 return 1951 1952 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1953 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1954 # searches for a proper new `__init__` in the MRO. The new `__init__` 1955 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1956 # instantiation of the protocol subclass will thus use the new 1957 # `__init__` and no longer call `_no_init_or_replace_init`. 1958 for base in cls.__mro__: 1959 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1960 if init is not _no_init_or_replace_init: 1961 cls.__init__ = init 1962 break 1963 else: 1964 # should not happen 1965 cls.__init__ = object.__init__ 1966 1967 cls.__init__(self, *args, **kwargs)
153@rustc_diagnostic_item("TryFrom") 154@typing.runtime_checkable 155class TryFrom(typing.Protocol[T_co, E]): 156 """Simple and safe type conversions that may fail in a controlled way under some circumstances. 157 It is the reciprocal of `TryInto`. 158 159 It is useful to implement this when you know that the conversion may fail in some way. 160 161 Generic Implementations 162 ------------------- 163 This interface takes two type arguments, and return `Result<Self, E>` 164 165 * `T`: Which's the first generic `T` is the type that's being converted from. 166 * `E`: If the conversion fails in a way, this is what will return as the error. 167 * `Self`: Which's the instance of the class that is being converted into. 168 169 Example 170 ------- 171 ```py 172 @dataclass 173 class Id(TryFrom[str, str]): 174 value: int 175 176 @classmethod 177 def try_from(cls, value: str) -> Result[Self, str]: 178 if not value.isnumeric(): 179 # NaN 180 return Err(f"Couldn't convert: {value} to self") 181 # otherwise convert it to an Id instance. 182 return Ok(value=cls(int(value))) 183 ``` 184 """ 185 186 __slots__ = () 187 188 @classmethod 189 def try_from(cls, value: T_co) -> Result[Self, E]: 190 """Perform the conversion.""" 191 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.
1941def _no_init_or_replace_init(self, *args, **kwargs): 1942 cls = type(self) 1943 1944 if cls._is_protocol: 1945 raise TypeError('Protocols cannot be instantiated') 1946 1947 # Already using a custom `__init__`. No need to calculate correct 1948 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1949 if cls.__init__ is not _no_init_or_replace_init: 1950 return 1951 1952 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1953 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1954 # searches for a proper new `__init__` in the MRO. The new `__init__` 1955 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1956 # instantiation of the protocol subclass will thus use the new 1957 # `__init__` and no longer call `_no_init_or_replace_init`. 1958 for base in cls.__mro__: 1959 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1960 if init is not _no_init_or_replace_init: 1961 cls.__init__ = init 1962 break 1963 else: 1964 # should not happen 1965 cls.__init__ = object.__init__ 1966 1967 cls.__init__(self, *args, **kwargs)
194@rustc_diagnostic_item("TryFrom") 195@typing.runtime_checkable 196class Into(typing.Protocol[T_cov]): 197 """Conversion from `self`, which may or may not be expensive. 198 199 Example 200 ------- 201 ```py 202 @dataclass 203 class Id(Into[str]): 204 value: int 205 206 def into(self) -> str: 207 return str(self.value) 208 ``` 209 """ 210 211 __slots__ = () 212 213 def into(self) -> T_cov: 214 """Perform the conversion.""" 215 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.
1941def _no_init_or_replace_init(self, *args, **kwargs): 1942 cls = type(self) 1943 1944 if cls._is_protocol: 1945 raise TypeError('Protocols cannot be instantiated') 1946 1947 # Already using a custom `__init__`. No need to calculate correct 1948 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1949 if cls.__init__ is not _no_init_or_replace_init: 1950 return 1951 1952 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1953 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1954 # searches for a proper new `__init__` in the MRO. The new `__init__` 1955 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1956 # instantiation of the protocol subclass will thus use the new 1957 # `__init__` and no longer call `_no_init_or_replace_init`. 1958 for base in cls.__mro__: 1959 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1960 if init is not _no_init_or_replace_init: 1961 cls.__init__ = init 1962 break 1963 else: 1964 # should not happen 1965 cls.__init__ = object.__init__ 1966 1967 cls.__init__(self, *args, **kwargs)
218@rustc_diagnostic_item("TryInto") 219@typing.runtime_checkable 220class TryInto(typing.Protocol[T, E]): 221 """An attempted conversion from `self`, which may or may not be expensive. 222 223 It is useful to implement this when you know that the conversion may fail in some way. 224 225 Generic Implementations 226 ------------------- 227 This interface takes two type arguments, and return `Result<T, E>` 228 229 * `T`: The first generic `T` is the type that's being converted into. 230 * `E`: If the conversion fails in a way, this is what will return as the error. 231 232 Example 233 ------- 234 ```py 235 @dataclass 236 class Id(TryInto[int, str]): 237 value: str 238 239 def try_into(self) -> Result[int, str]: 240 if not self.value.isnumeric(): 241 return Err(f"{self.value} is not a number...") 242 return Ok(int(self.value)) 243 ``` 244 """ 245 246 __slots__ = () 247 248 def try_into(self) -> Result[T, E]: 249 """Perform the conversion.""" 250 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.
1941def _no_init_or_replace_init(self, *args, **kwargs): 1942 cls = type(self) 1943 1944 if cls._is_protocol: 1945 raise TypeError('Protocols cannot be instantiated') 1946 1947 # Already using a custom `__init__`. No need to calculate correct 1948 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1949 if cls.__init__ is not _no_init_or_replace_init: 1950 return 1951 1952 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1953 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1954 # searches for a proper new `__init__` in the MRO. The new `__init__` 1955 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1956 # instantiation of the protocol subclass will thus use the new 1957 # `__init__` and no longer call `_no_init_or_replace_init`. 1958 for base in cls.__mro__: 1959 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1960 if init is not _no_init_or_replace_init: 1961 cls.__init__ = init 1962 break 1963 else: 1964 # should not happen 1965 cls.__init__ = object.__init__ 1966 1967 cls.__init__(self, *args, **kwargs)
253@rustc_diagnostic_item("ToString") 254@typing.runtime_checkable 255class ToString(typing.Protocol): 256 """A trait for explicitly converting a value to a `str`. 257 258 Example 259 ------- 260 ```py 261 class Value[T: bytes](ToString): 262 buffer: T 263 264 def to_string(self) -> str: 265 return self.buffer.decode("utf-8") 266 ``` 267 """ 268 269 __slots__ = () 270 271 def to_string(self) -> str: 272 """Converts the given value to a `str`. 273 274 Example 275 -------- 276 ```py 277 i = 5 # assume `int` implements `ToString` 278 five = "5" 279 assert five == i.to_string() 280 ``` 281 """ 282 raise NotImplementedError 283 284 def __str__(self) -> str: 285 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.
1941def _no_init_or_replace_init(self, *args, **kwargs): 1942 cls = type(self) 1943 1944 if cls._is_protocol: 1945 raise TypeError('Protocols cannot be instantiated') 1946 1947 # Already using a custom `__init__`. No need to calculate correct 1948 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1949 if cls.__init__ is not _no_init_or_replace_init: 1950 return 1951 1952 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1953 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1954 # searches for a proper new `__init__` in the MRO. The new `__init__` 1955 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1956 # instantiation of the protocol subclass will thus use the new 1957 # `__init__` and no longer call `_no_init_or_replace_init`. 1958 for base in cls.__mro__: 1959 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1960 if init is not _no_init_or_replace_init: 1961 cls.__init__ = init 1962 break 1963 else: 1964 # should not happen 1965 cls.__init__ = object.__init__ 1966 1967 cls.__init__(self, *args, **kwargs)
271 def to_string(self) -> str: 272 """Converts the given value to a `str`. 273 274 Example 275 -------- 276 ```py 277 i = 5 # assume `int` implements `ToString` 278 five = "5" 279 assert five == i.to_string() 280 ``` 281 """ 282 raise NotImplementedError
Converts the given value to a str
.
Example
i = 5 # assume `int` implements `ToString`
five = "5"
assert five == i.to_string()