sain
sain — Write safe Python code like Rust 🦀
sain is a dependency-free Python library which brings the Rust ecosystem to Python, Allowing its users to write safe, idiomatic Python code just like Rust.
What sain provides
If you already know what you are looking for, the fastest way is to use the search bar,
otherwise, you may want to jump into one of these useful sections:
- Fundamental types, such as slice.
- collections Implementations of the most common general purpose data structures from Rust's
std::collections
and friends such as HashMap, Vec
. - Core error-handling types such as
Option
andSome
variant,Result
andOk
,Err
variants. - The
Iterator
trait and its adapters. - Common traits such as
From
,Into
. - Synchronization primitives, includes LazyLock and Once.
- Support macros such as
#[deprecated]
,todo!()
,unimplemented!()
and more.
A Tour of sain
The next section will include the most general purpose and notable features of Rust implemented in sain.
Containers and Collections
The option and result modules define optional and error-handling types,
Option
For std::collections
types, sain exposes Vec
to the top level, which can be imported directly, as for the rest of the types,
they all exist under sain.collections
, notable ones are:
1# BSD 3-Clause License 2# 3# Copyright (c) 2022-Present, nxtlo 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# * Redistributions of source code must retain the above copyright notice, this 10# list of conditions and the following disclaimer. 11# 12# * Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# * Neither the name of the copyright holder nor the names of its 17# contributors may be used to endorse or promote products derived from 18# this software without specific prior written permission. 19# 20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31""" 32.. include:: ../DOCS.md 33""" 34 35from __future__ import annotations 36 37__all__ = ( 38 # async_iter.py 39 "async_iter", 40 # cfg.py 41 "cfg", 42 "cfg_attr", 43 # default.py 44 "default", 45 "Default", 46 # option.py 47 "option", 48 "Some", 49 "Option", 50 "NOTHING", 51 # iter.py 52 "Iter", 53 "Iterator", 54 "iter", 55 # macros.py 56 "macros", 57 "todo", 58 "deprecated", 59 "unimplemented", 60 "doc", 61 "include_str", 62 "include_bytes", 63 "assert_eq", 64 "assert_ne", 65 # futures.py 66 "futures", 67 # result.py 68 "result", 69 "Ok", 70 "Err", 71 "Result", 72 # collections 73 "collections", 74 "Vec", 75 # error.py 76 "error", 77 "Error", 78 # boxed.py 79 "boxed", 80 "Box", 81 # sync 82 "sync", 83 # maybe_uninit.py 84 "maybe_uninit", 85 # convert 86 "convert", 87 "From", 88 "TryFrom", 89 "Into", 90 "TryInto", 91 "ToString", 92 # time 93 "time", 94 # misc 95 "__version__", 96 "__url__", 97 "__author__", 98 "__about__", 99 "__license__", 100) 101 102from . import async_iter 103from . import boxed 104from . import collections 105from . import convert 106from . import default 107from . import error 108from . import futures 109from . import iter 110from . import macros 111from . import maybe_uninit 112from . import option 113from . import result 114from . import sync 115from . import time 116from ._misc import __about__ 117from ._misc import __author__ 118from ._misc import __license__ 119from ._misc import __url__ 120from ._misc import __version__ 121from .boxed import Box 122from .cfg import cfg 123from .cfg import cfg_attr 124from .collections import Vec 125from .convert import From 126from .convert import Into 127from .convert import ToString 128from .convert import TryFrom 129from .convert import TryInto 130from .default import Default 131from .error import Error 132from .iter import Iter 133from .iter import Iterator 134from .macros import assert_eq 135from .macros import assert_ne 136from .macros import deprecated 137from .macros import doc 138from .macros import include_bytes 139from .macros import include_str 140from .macros import todo 141from .macros import unimplemented 142from .option import NOTHING 143from .option import Option 144from .option import Some 145from .result import Err 146from .result import Ok 147from .result import Result
174@rustc_diagnostic_item("cfg") 175def cfg( 176 target_os: System | None = None, 177 python_version: tuple[int, ...] | None = None, 178 target_arch: Arch | None = None, 179 impl: Python | None = None, 180) -> bool: 181 """A function that will run the code only if all predicate attributes returns `True`. 182 183 The difference between this function and `cfg_attr` is that this function will not raise an exception. 184 Instead it will return `False` if any of the attributes fails. 185 186 Example 187 ------- 188 ```py 189 import sain 190 191 if cfg(target_os="windows"): 192 print("Windows") 193 elif cfg(target_os="linux", target_arch="arm64"): 194 print("Linux") 195 else: 196 print("Something else") 197 ``` 198 199 Parameters 200 ---------- 201 target_os : `str | None` 202 The targeted operating system that's required for the object to be executed. 203 python_version : `tuple[int, ...] | None` 204 The targeted Python version that's required for the object to be executed. Format must be `(3, ..., ...)` 205 target_arch : `str | None` 206 The CPU targeted architecture that's required for the object to be executed. 207 impl : `str | None` 208 The Python implementation that's required for the object to be executed. 209 210 Returns 211 ------- 212 `bool` 213 The condition that was checked. 214 """ 215 checker = _AttrCheck( 216 lambda: None, 217 no_raise=True, 218 target_os=target_os, 219 python_version=python_version, 220 target_arch=target_arch, 221 impl=impl, 222 ) 223 return checker.check_once()
A function that will run the code only if all predicate attributes returns True
.
The difference between this function and cfg_attr
is that this function will not raise an exception.
Instead it will return False
if any of the attributes fails.
Example
import sain
if cfg(target_os="windows"):
print("Windows")
elif cfg(target_os="linux", target_arch="arm64"):
print("Linux")
else:
print("Something else")
Parameters
- target_os (
str | None
): The targeted operating system that's required for the object to be executed. - python_version (
tuple[int, ...] | None
): The targeted Python version that's required for the object to be executed. Format must be(3, ..., ...)
- target_arch (
str | None
): The CPU targeted architecture that's required for the object to be executed. - impl (
str | None
): The Python implementation that's required for the object to be executed.
Returns
bool
: The condition that was checked.
Implementations
This function implements cfg in Rust.
102@rustc_diagnostic_item("cfg_attr") 103def cfg_attr( 104 *, 105 target_os: System | None = None, 106 python_version: tuple[int, ...] | None = None, 107 target_arch: Arch | None = None, 108 impl: Python | None = None, 109) -> collections.Callable[[F], F]: 110 """Conditional runtime object configuration based on passed arguments. 111 112 If the decorated object gets called and one of the attributes returns `False`, 113 `RuntimeError` will be raised and the object will not run. 114 115 Example 116 ------- 117 ```py 118 import sain 119 120 @cfg_attr(target_os="windows") 121 def windows_only(): 122 # Do stuff with Windows's API. 123 ... 124 125 # Mut be PyPy Python implementation or `RuntimeError` will be raised 126 # when creating the instance. 127 @cfg_attr(impl="PyPy") 128 class Zoo: 129 @sain.cfg_attr(target_os="linux") 130 def bark(self) -> None: 131 ... 132 133 # An instance will not be created if raised. 134 zoo = Zoo() 135 # RuntimeError("class Zoo requires PyPy implementation") 136 ``` 137 138 Parameters 139 ---------- 140 target_os : `str | None` 141 The targeted operating system that's required for the object. 142 python_version : `tuple[int, int, int] | None` 143 The targeted Python version that's required for the object. Format must be `(3, ..., ...)`. 144 target_arch : `str | None` 145 The CPU targeted architecture that's required for the object. 146 impl : `str | None` 147 The Python implementation that's required for the object. 148 149 Raises 150 ------ 151 `RuntimeError` 152 This fails if any of the attributes returns `False`. 153 `ValueError` 154 If the passed Python implementation is unknown. 155 """ 156 157 def decorator(callback: F) -> F: 158 @functools.wraps(callback) 159 def wrapper(*args: typing.Any, **kwargs: typing.Any) -> F: 160 checker = _AttrCheck( 161 callback, 162 target_os=target_os, 163 python_version=python_version, 164 target_arch=target_arch, 165 impl=impl, 166 ) 167 return checker(*args, **kwargs) 168 169 return typing.cast(F, wrapper) 170 171 return decorator
Conditional runtime object configuration based on passed arguments.
If the decorated object gets called and one of the attributes returns False
,
RuntimeError
will be raised and the object will not run.
Example
import sain
@cfg_attr(target_os="windows")
def windows_only():
# Do stuff with Windows's API.
...
# Mut be PyPy Python implementation or `RuntimeError` will be raised
# when creating the instance.
@cfg_attr(impl="PyPy")
class Zoo:
@sain.cfg_attr(target_os="linux")
def bark(self) -> None:
...
# An instance will not be created if raised.
zoo = Zoo()
# RuntimeError("class Zoo requires PyPy implementation")
Parameters
- target_os (
str | None
): The targeted operating system that's required for the object. - python_version (
tuple[int, int, int] | None
): The targeted Python version that's required for the object. Format must be(3, ..., ...)
. - target_arch (
str | None
): The CPU targeted architecture that's required for the object. - impl (
str | None
): The Python implementation that's required for the object.
Raises
RuntimeError
: This fails if any of the attributes returnsFalse
.ValueError
: If the passed Python implementation is unknown.
Implementations
This function implements cfg_attr in Rust.
59@rustc_diagnostic_item("Default") 60@typing.runtime_checkable 61class Default(typing.Protocol[_T_co]): 62 """An interface for an object that has a default value. 63 64 Example 65 ------- 66 ```py 67 from sain import Default 68 69 class Cache(Default[dict[str, Any]]): 70 71 @staticmethod 72 def default() -> dict[str, Any]: 73 return {} 74 75 cache = Cache.default() 76 print(cache) 77 assert isinstance(cache, Default) 78 # {} 79 ``` 80 """ 81 82 __slots__ = () 83 84 # FIXME: `impl Default for String` knows the type of `Self` is `String` but we can't do that. 85 # So generics is the only way to achieve the same effect. But `Default` in Rust is not generic. 86 # in the future we just swap to `Self`. 87 @staticmethod 88 @rustc_diagnostic_item("default_fn") 89 def default() -> _T_co: 90 """Return the default value of the object.""" 91 raise NotImplementedError
An interface for an object that has a default value.
Example
from sain import Default
class Cache(Default[dict[str, Any]]):
@staticmethod
def default() -> dict[str, Any]:
return {}
cache = Cache.default()
print(cache)
assert isinstance(cache, Default)
# {}
Implementations
This class implements Default in Rust.
1945def _no_init_or_replace_init(self, *args, **kwargs): 1946 cls = type(self) 1947 1948 if cls._is_protocol: 1949 raise TypeError('Protocols cannot be instantiated') 1950 1951 # Already using a custom `__init__`. No need to calculate correct 1952 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1953 if cls.__init__ is not _no_init_or_replace_init: 1954 return 1955 1956 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1957 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1958 # searches for a proper new `__init__` in the MRO. The new `__init__` 1959 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1960 # instantiation of the protocol subclass will thus use the new 1961 # `__init__` and no longer call `_no_init_or_replace_init`. 1962 for base in cls.__mro__: 1963 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1964 if init is not _no_init_or_replace_init: 1965 cls.__init__ = init 1966 break 1967 else: 1968 # should not happen 1969 cls.__init__ = object.__init__ 1970 1971 cls.__init__(self, *args, **kwargs)
87 @staticmethod 88 @rustc_diagnostic_item("default_fn") 89 def default() -> _T_co: 90 """Return the default value of the object.""" 91 raise NotImplementedError
Return the default value of the object.
Implementations
This function implements .Default.html#tymethodsain.default">default_fn in Rust.
56@rustc_diagnostic_item("Option") 57@typing.final 58class Some( 59 typing.Generic[T], 60 _default.Default["Option[T]"], 61): 62 """The `Option` type represents optional value, higher-level abstraction over the `None` type. 63 64 It combines union of `T | None` in one convenient structure, allowing the users to manipulate and propagate 65 the contained value idiomatically. 66 67 An `Option` value have multiple use cases: 68 69 * Initial values. 70 * Return value for functions that may or may not contain a return value. 71 * Optional parameters, class fields. 72 * Swapping values. 73 74 Example 75 ------- 76 ```py 77 # the actual implementation of the object. 78 from sain import Some 79 # Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like. 80 from sain import Option 81 82 def divide(numerator: float, denominator: float) -> Option[float]: 83 if denominator == 0.0: 84 return Some(None) 85 return Some(numerator / denominator) 86 87 # Returns Option[float] 88 result = divide(2.0, 3.0) 89 90 # Pattern match to retrieve the value 91 match result: 92 # The division is valid. 93 case Some(x): 94 print("Result:", x) 95 # Invalid division, this is Some(None) 96 case _: 97 print("cannot divide by 0") 98 ``` 99 100 ### Converting `None`s into `RuntimeError`s 101 102 Sometimes we want to extract the value and cause an error to the caller if it doesn't exist, 103 104 because handling `Some/None` can be tedious, luckily we have several ways to deal with this. 105 106 ```py 107 def ipaddr(s: str) -> Option[tuple[int, int, int, int]]: 108 match s.split('.'): 109 case [a, b, c, d]: 110 return Some((int(a), int(b), int(c), int(d))) 111 case _: 112 return Some(None) 113 114 # calls `unwrap()` for you. 115 ip = ~ipaddr("192.168.1.19") 116 # causes a `RuntimeError` if it returns `None`. 117 ip = ipaddr("192.168.1.19").unwrap() 118 # causes a `RuntimeError` if it returns `None`, with a context message. 119 ip = ipaddr("192.168.1.19").expect("i need an ip address :<") 120 ``` 121 122 The `~` operator will result in `tuple[int, int, int, int]` if the parsing succeed. 123 unless the contained value is `None`, it will cause a `RuntimeError`. 124 125 If the value must be present, use `unwrap_or`, which takes a default parameter 126 and returns it in-case `ipaddr` returns `None` 127 ```py 128 ip = ipaddr("blah blah blah").unwrap_or("192.168.1.255") 129 # Results: 192.168.1.255 130 ``` 131 132 Overall, this type provides many other functional methods such as `map`, `filter`. 133 134 boolean checks such as `is_some`, `is_none`, converting between `Option` and `Result` using `ok_or`, and many more. 135 """ 136 137 __slots__ = ("_value",) 138 __match_args__ = ("_value",) 139 140 def __init__(self, value: T | None, /) -> None: 141 self._value = value 142 143 @staticmethod 144 def default() -> Option[T]: 145 """Default value for `Option<T>`. Returns `None` wrapped in `Some`. 146 147 Example 148 ------- 149 ```py 150 assert Some[int].default() is NOTHING 151 ``` 152 """ 153 return NOTHING 154 155 # *- Reading the value -* 156 157 def transpose(self) -> T | None: 158 """Convert `Option[T]` into `T | None`. 159 160 Examples 161 -------- 162 ```py 163 opt = Some('char') 164 x = opt.transpose() 165 assert x == 'char' 166 167 opt = Some(None) 168 assert opt.transpose() is None 169 ``` 170 """ 171 return self._value 172 173 def unwrap(self) -> T: 174 """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`. 175 176 It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns. 177 178 Example 179 ------- 180 ```py 181 value = Some(5) 182 print(value.unwrap()) 183 # 5 184 185 value = Some(None) 186 print(value.unwrap()) 187 # RuntimeError 188 ``` 189 190 Raises 191 ------ 192 `RuntimeError` 193 If the inner value is `None`. 194 """ 195 if self._value is None: 196 raise RuntimeError("Called `Option.unwrap()` on `None`.") from None 197 198 return self._value 199 200 def unwrap_or(self, default: T, /) -> T: 201 """Unwrap the inner value either returning if its not `None` or returning `default`. 202 203 Example 204 ------- 205 ```py 206 value = Some(5) 207 print(value.unwrap_or(10)) 208 # 5 209 210 # Type hint is required here. 211 value: Option[int] = Some(None) 212 print(value.unwrap_or(10)) 213 # 10 214 ``` 215 """ 216 if self._value is None: 217 return default 218 219 return self._value 220 221 def unwrap_or_else(self, f: FnOnce[T], /) -> T: 222 """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value. 223 224 Example 225 ------- 226 ```py 227 value = Some(5) 228 print(value.unwrap_or_else(lambda: 10)) 229 # 5 230 231 value: Option[bool] = Some(None) 232 print(value.unwrap_or_else(lambda: True)) 233 # True 234 ``` 235 """ 236 if self._value is None: 237 return f() 238 239 return self._value 240 241 @macros.unsafe 242 def unwrap_unchecked(self) -> T: 243 """Returns the contained Some value without checking that the value is not None. 244 245 Example 246 ------- 247 ```py 248 v: Option[float] = Some(1.2) 249 v.unwrap_unchecked() # 1.2 250 251 v: Option[float] = Some(None) 252 print(v.unwrap_unchecked()) # Undefined Behavior 253 ``` 254 """ 255 #! SAFETY: The caller guarantees that the value is not None. 256 return self._value # pyright: ignore 257 258 def expect(self, message: str, /) -> T: 259 """Returns the contained `Some` value. 260 261 raises if the value is `None` with a custom provided `message`. 262 263 Example 264 ------- 265 ```py 266 value = Some("Hello") 267 268 print(value.expect("Value is None")) 269 # "Hello" 270 271 value: Option[str] = Some(None) 272 print(value.expect("Value is None")) 273 # RuntimeError("Value is None") 274 ``` 275 """ 276 if self._value is None: 277 raise RuntimeError(message) 278 279 return self._value 280 281 # *- object transformation -* 282 283 def map(self, f: Fn[T, U], /) -> Option[U]: 284 """Map the inner value to another type. Returning `Some(None)` if `T` is `None`. 285 286 Example 287 ------- 288 ```py 289 value = Some(5.0) 290 291 print(value.map(lambda x: x * 2.0)) 292 # Some(10.0) 293 294 value: Option[bool] = Some(None) 295 print(value) 296 # Some(None) 297 ``` 298 """ 299 if self._value is None: 300 return NOTHING 301 302 return Some(f(self._value)) 303 304 def map_or(self, default: U, f: Fn[T, U], /) -> U: 305 """Map the inner value to another type or return `default` if its `None`. 306 307 Example 308 ------- 309 ```py 310 value: Option[float] = Some(5.0) 311 312 # map to int. 313 print(value.map_or(0, int)) 314 # 6 315 316 value: Option[float] = Some(None) 317 print(value.map_or(0, int) 318 # 0 319 ``` 320 """ 321 if self._value is None: 322 return default 323 324 return f(self._value) 325 326 def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U: 327 """Map the inner value to another type, or return `default()` if its `None`. 328 329 Example 330 ------- 331 ```py 332 def default() -> int: 333 return sys.getsizeof(object()) 334 335 value: Option[float] = Some(5.0) 336 337 # map to int. 338 print(value.map_or_else(default, int)) 339 # 6 340 341 value: Option[float] = Some(None) 342 print(value.map_or_else(default, int) 343 # 28 <- size of object() 344 ``` 345 """ 346 if self._value is None: 347 return default() 348 349 return f(self._value) 350 351 def filter(self, predicate: Fn[T, bool]) -> Option[T]: 352 """Returns `Some(None)` if the contained value is `None`, 353 354 otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`. 355 356 Example 357 ------- 358 ```py 359 value = Some([1, 2, 3]) 360 361 print(value.filter(lambda x: 1 in x)) 362 # Some([1, 2, 3]) 363 364 value: Option[int] = Some([1, 2, 3]) # or Some(None) 365 print(value.filter(lambda x: 1 not in x)) 366 # None 367 ``` 368 """ 369 if (value := self._value) is not None: 370 if predicate(value): 371 return Some(value) 372 373 return NOTHING 374 375 def ok_or(self, err: U) -> _result.Result[T, U]: 376 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. 377 378 Example 379 ------- 380 ```py 381 xyz: Option[str] = Some("foo") 382 assert xyz.ok_or(None) == Ok("foo") 383 384 xyz: Option[str] = Some(None) 385 assert xyz.ok_or(None) == Err(None) 386 ``` 387 """ 388 if self._value is None: 389 return _result.Err(err) 390 391 return _result.Ok(self._value) 392 393 def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]: 394 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. 395 396 Example 397 ------- 398 ```py 399 xyz: Option[str] = Some("foo") 400 assert xyz.ok_or(None) == Ok("foo") 401 402 xyz: Option[str] = Some(None) 403 assert xyz.ok_or(None) == Err(None) 404 ``` 405 """ 406 if self._value is None: 407 return _result.Err(err()) 408 409 return _result.Ok(self._value) 410 411 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 412 """Zips `self` with `other`. 413 414 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`. 415 416 Example 417 ------- 418 ```py 419 x = Some(1) 420 y = Some("hi") 421 z: Option[str] = Some(None) 422 423 assert x.zip(y) == Some((1, "hi")) 424 assert x.zip(z) == Some(None) 425 ``` 426 """ 427 if self._value is not None and other._value is not None: 428 return Some((self._value, other._value)) 429 430 return NOTHING 431 432 def zip_with( 433 self, other: Option[U], f: collections.Callable[[T, U], T_co] 434 ) -> Option[T_co]: 435 """Zips `self` with `other` using function `f`. 436 437 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`. 438 439 Example 440 ------- 441 ```py 442 @dataclass 443 class Point: 444 x: float 445 y: float 446 447 x, y = Some(32.1), Some(42.4) 448 assert x.zip_with(y, Point) == Some(Point(32.1, 42.4)) 449 ``` 450 """ 451 if self._value is not None and other._value is not None: 452 return Some(f(self._value, other._value)) 453 454 return NOTHING 455 456 # *- Inner operations *- 457 458 def take(self) -> Option[T]: 459 """Take the value from `self` Setting it to `None`, and then return `Some(v)`. 460 461 If you don't care about the original value, use `Option.clear()` instead. 462 463 Example 464 ------- 465 ```py 466 original = Some("Hi") 467 new = original.take() 468 469 print(original, new) 470 # None, Some("Hi") 471 ``` 472 """ 473 if self._value is None: 474 return NOTHING 475 476 val = self._value 477 self._value = None 478 return Some(val) 479 480 def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]: 481 """Take the value from `Self`, Setting it to `None` only if predicate returns `True`. 482 483 If you don't care about the original value, use `Option.clear_if()` instead. 484 485 Example 486 ------- 487 ```py 488 def validate(email: str) -> bool: 489 # you can obviously validate this better. 490 return email.find('@') == 1 491 492 original = Some("flex@gg.com") 493 valid = original.take_if(validate) 494 assert is_allowed.is_some() and original.is_none() 495 496 original = Some("mail.example.com") 497 invalid = original.take_if(validate) 498 assert invalid.is_none() and original.is_some() 499 ``` 500 """ 501 if self.map_or(False, predicate): 502 return self.take() 503 504 return NOTHING 505 506 def clear(self) -> None: 507 """Clear the inner value, setting it to `None`. 508 509 If you care about the original value, use `Option.take()` instead. 510 511 Example 512 ------- 513 ```py 514 value = Some("Hello") 515 value.clear() 516 assert value.is_none() 517 ``` 518 """ 519 self._value = None 520 521 def clear_if(self, predicate: Fn[T, bool]) -> None: 522 """Clear the inner value, setting it to `None` if the predicate returns `True`. 523 524 If you care about the original value, use `Option.take_if()` instead. 525 526 Example 527 ------- 528 ```py 529 value = Some("Hello") 530 value.clear_if(lambda x: x == "Hello") 531 assert value.is_none() 532 ``` 533 """ 534 if self._value is not None and predicate(self._value): 535 self._value = None 536 537 def replace(self, value: T) -> Option[T]: 538 """Replace the contained value with another value. 539 540 Use `Option.insert` if you want to return the original value 541 that got inserted instead of `self` 542 543 Example 544 ------- 545 ```py 546 value: Option[str] = Some(None) 547 value.replace("Hello") 548 # Some("Hello") 549 ``` 550 """ 551 self._value = value 552 return self 553 554 def insert(self, value: T) -> T: 555 """Insert a value into the option, and then return a reference to it. 556 557 This will overwrite the old value if it was already contained. 558 559 Example 560 ------- 561 ```py 562 flag: Option[bool] = Some(None) 563 flag_ref = flag.insert(True) 564 assert flag_ref == True 565 assert flag.unwrap() == True 566 ``` 567 """ 568 self._value = value 569 return value 570 571 def get_or_insert(self, value: T) -> T: 572 """Insert a value into the option if it was `None`, 573 and then return a reference to it. 574 575 Example 576 ------- 577 ```py 578 state: Option[bool] = Some(None) 579 assert state.get_or_insert(True) is True 580 assert state.get_or_insert(False) is True 581 ``` 582 """ 583 if self._value is not None: 584 return self._value 585 586 self._value = value 587 return value 588 589 def get_or_insert_with(self, f: FnOnce[T]) -> T: 590 """Insert a value into the option computed from `f()` if it was `None`, 591 and then return a reference to it. 592 593 Example 594 ------- 595 ```py 596 flag: Option[bool] = Some(None) 597 flag_ref = flag.insert(True) 598 assert flag_ref == True 599 assert flag.unwrap() == True 600 ``` 601 """ 602 if self._value is not None: 603 return self._value 604 605 v = self._value = f() 606 return v 607 608 def and_ok(self, optb: Option[T]) -> Option[T]: 609 """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`. 610 611 aliases: `Option::and` 612 613 Example 614 ------- 615 ```py 616 x = Some(1) 617 y: Option[str] = Some(None) 618 assert x.and_ok(y) == Some(None) 619 620 x: Option[str] = Some(None) 621 y = Some(1) 622 assert x.and_ok(y) == Some(None) 623 624 x: Option[str] = Some("hi") 625 y = Some(100) 626 assert x.and_ok(y) == Some(100) 627 ``` 628 """ 629 if self._value is None or optb._value is None: 630 return optb 631 632 return NOTHING 633 634 def and_then(self, f: Fn[T, Option[T]]) -> Option[T]: 635 """Returns `Some(None)` if the contained value is `None`, otherwise call `f()` 636 on `T` and return `Option[T]`. 637 638 Example 639 ------- 640 ```py 641 value = Some(5) 642 print(value.and_then(lambda x: Some(x * 2))) 643 # Some(10) 644 645 value: Option[int] = Some(None) 646 print(value.and_then(lambda x: Some(x * 2))) 647 # Some(None) 648 ``` 649 """ 650 if self._value is None: 651 return NOTHING 652 653 return f(self._value) 654 655 def inspect(self, f: Fn[T, typing.Any]) -> Option[T]: 656 """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing. 657 658 Example 659 ------- 660 ```py 661 def debug(x: str) -> None: 662 print("Debugging:", x) 663 664 value = Some("foo") 665 inner = value.inspect(debug).expect("no value to debug") 666 # prints: Debugging: "foo" 667 668 value: Option[str] = Some(None) 669 value.inspect(debug) # prints nothing 670 """ 671 if self._value is not None: 672 f(self._value) 673 674 return self 675 676 # *- Builder methods *- 677 678 def iter(self) -> _iter.ExactSizeIterator[T]: 679 """Returns an iterator over the contained value. 680 681 Example 682 ------- 683 ```py 684 from sain import Some 685 value = Some("gg").iter() 686 assert value.next() == Some("gg") 687 688 value: Option[int] = Some(None) 689 assert value.iter().next().is_none() 690 ``` 691 """ 692 if self._value is None: 693 return _iter.empty() 694 695 return _iter.once(self._value) 696 697 # *- Boolean checks *- 698 699 def is_some(self) -> bool: 700 """Returns `True` if the contained value is not `None`, otherwise returns `False`. 701 702 Example 703 ------- 704 ```py 705 value = Some(5) 706 print(value.is_some()) 707 # True 708 709 value: Option[int] = Some(None) 710 print(value.is_some()) 711 # False 712 ``` 713 """ 714 return self._value is not None 715 716 def is_some_and(self, predicate: Fn[T, bool]) -> bool: 717 """Returns `True` if the contained value is not `None` and 718 the predicate returns `True`, otherwise returns `False`. 719 720 Example 721 ------- 722 ```py 723 value = Some(5) 724 print(value.is_some_and(lambda x: x > 3)) 725 # True 726 727 value: Option[int] = Some(None) 728 print(value.is_some_and(lambda x: x > 3)) 729 # False 730 ``` 731 """ 732 return self._value is not None and predicate(self._value) 733 734 def is_none(self) -> bool: 735 """Returns `True` if the contained value is `None`, otherwise returns `False`. 736 737 Example 738 ------- 739 ```py 740 value = Some(5) 741 print(value.is_none()) 742 # False 743 744 value: Option[int] = Some(None) 745 print(value.is_none()) 746 # True 747 ``` 748 """ 749 return self._value is None 750 751 def is_none_or(self, f: Fn[T, bool]) -> bool: 752 """Returns `True` if the contained value is `None` or the predicate returns `True`, 753 otherwise returns `False`. 754 755 Example 756 ------- 757 ```py 758 value = Some(5) 759 print(value.is_none_or(lambda x: x > 3)) 760 # False 761 762 value: Option[int] = Some(None) 763 print(value.is_none_or(lambda x: x > 3)) 764 # True 765 ``` 766 """ 767 match self._value: 768 case None: 769 return True 770 case x: 771 return f(x) 772 773 def __repr__(self) -> str: 774 if self._value is None: 775 return "None" 776 return f"Some({self._value!r})" 777 778 __str__ = __repr__ 779 780 def __invert__(self) -> T: 781 return self.unwrap() 782 783 def __or__(self, other: T) -> T: 784 return self._value if self._value is not None else other 785 786 def __bool__(self) -> bool: 787 return self._value is not None 788 789 def __eq__(self, other: None | object) -> bool: 790 if other is None: 791 return self._value is None 792 793 if not isinstance(other, Some): 794 return NotImplemented 795 796 return self._value == other._value # pyright: ignore[reportUnknownVariableType, reportUnknownMemberType] 797 798 def __ne__(self, other: object) -> bool: 799 return not self.__eq__(other) 800 801 def __hash__(self) -> int: 802 return hash(self._value)
The Option
type represents optional value, higher-level abstraction over the None
type.
It combines union of T | None
in one convenient structure, allowing the users to manipulate and propagate
the contained value idiomatically.
An Option
value have multiple use cases:
- Initial values.
- Return value for functions that may or may not contain a return value.
- Optional parameters, class fields.
- Swapping values.
Example
# the actual implementation of the object.
from sain import Some
# Here `Option` is used for type-hints only, you can include it under `TYPE_CHECKING` if you'd like.
from sain import Option
def divide(numerator: float, denominator: float) -> Option[float]:
if denominator == 0.0:
return Some(None)
return Some(numerator / denominator)
# Returns Option[float]
result = divide(2.0, 3.0)
# Pattern match to retrieve the value
match result:
# The division is valid.
case Some(x):
print("Result:", x)
# Invalid division, this is Some(None)
case _:
print("cannot divide by 0")
Converting None
s into RuntimeError
s
Sometimes we want to extract the value and cause an error to the caller if it doesn't exist,
because handling Some/None
can be tedious, luckily we have several ways to deal with this.
def ipaddr(s: str) -> Option[tuple[int, int, int, int]]:
match s.split('.'):
case [a, b, c, d]:
return Some((int(a), int(b), int(c), int(d)))
case _:
return Some(None)
# calls `unwrap()` for you.
ip = ~ipaddr("192.168.1.19")
# causes a `RuntimeError` if it returns `None`.
ip = ipaddr("192.168.1.19").unwrap()
# causes a `RuntimeError` if it returns `None`, with a context message.
ip = ipaddr("192.168.1.19").expect("i need an ip address :<")
The ~
operator will result in tuple[int, int, int, int]
if the parsing succeed.
unless the contained value is None
, it will cause a RuntimeError
.
If the value must be present, use unwrap_or
, which takes a default parameter
and returns it in-case ipaddr
returns None
ip = ipaddr("blah blah blah").unwrap_or("192.168.1.255")
# Results: 192.168.1.255
Overall, this type provides many other functional methods such as map
, filter
.
boolean checks such as is_some
, is_none
, converting between Option
and Result
using ok_or
, and many more.
Implementations
This class implements Option in Rust.
143 @staticmethod 144 def default() -> Option[T]: 145 """Default value for `Option<T>`. Returns `None` wrapped in `Some`. 146 147 Example 148 ------- 149 ```py 150 assert Some[int].default() is NOTHING 151 ``` 152 """ 153 return NOTHING
Default value for Option<T>
. Returns None
wrapped in Some
.
Example
assert Some[int]sain.default() is NOTHING
157 def transpose(self) -> T | None: 158 """Convert `Option[T]` into `T | None`. 159 160 Examples 161 -------- 162 ```py 163 opt = Some('char') 164 x = opt.transpose() 165 assert x == 'char' 166 167 opt = Some(None) 168 assert opt.transpose() is None 169 ``` 170 """ 171 return self._value
Convert Option[T]
into T | None
.
Examples
opt = Some('char')
x = opt.transpose()
assert x == 'char'
opt = Some(None)
assert opt.transpose() is None
173 def unwrap(self) -> T: 174 """Unwrap the inner value either returning if its not `None` or raising a `RuntimeError`. 175 176 It's usually not recommended to use this method in production code, and instead use safer options such as `unwrap_or` or match patterns. 177 178 Example 179 ------- 180 ```py 181 value = Some(5) 182 print(value.unwrap()) 183 # 5 184 185 value = Some(None) 186 print(value.unwrap()) 187 # RuntimeError 188 ``` 189 190 Raises 191 ------ 192 `RuntimeError` 193 If the inner value is `None`. 194 """ 195 if self._value is None: 196 raise RuntimeError("Called `Option.unwrap()` on `None`.") from None 197 198 return self._value
Unwrap the inner value either returning if its not None
or raising a RuntimeError
.
It's usually not recommended to use this method in production code, and instead use safer options such as unwrap_or
or match patterns.
Example
value = Some(5)
print(value.unwrap())
# 5
value = Some(None)
print(value.unwrap())
# RuntimeError
Raises
RuntimeError
: If the inner value isNone
.
200 def unwrap_or(self, default: T, /) -> T: 201 """Unwrap the inner value either returning if its not `None` or returning `default`. 202 203 Example 204 ------- 205 ```py 206 value = Some(5) 207 print(value.unwrap_or(10)) 208 # 5 209 210 # Type hint is required here. 211 value: Option[int] = Some(None) 212 print(value.unwrap_or(10)) 213 # 10 214 ``` 215 """ 216 if self._value is None: 217 return default 218 219 return self._value
Unwrap the inner value either returning if its not None
or returning default
.
Example
value = Some(5)
print(value.unwrap_or(10))
# 5
# Type hint is required here.
value: Option[int] = Some(None)
print(value.unwrap_or(10))
# 10
221 def unwrap_or_else(self, f: FnOnce[T], /) -> T: 222 """Unwrap the inner value either returning if its not `None` or calling `f` to get a default value. 223 224 Example 225 ------- 226 ```py 227 value = Some(5) 228 print(value.unwrap_or_else(lambda: 10)) 229 # 5 230 231 value: Option[bool] = Some(None) 232 print(value.unwrap_or_else(lambda: True)) 233 # True 234 ``` 235 """ 236 if self._value is None: 237 return f() 238 239 return self._value
Unwrap the inner value either returning if its not None
or calling f
to get a default value.
Example
value = Some(5)
print(value.unwrap_or_else(lambda: 10))
# 5
value: Option[bool] = Some(None)
print(value.unwrap_or_else(lambda: True))
# True
241 @macros.unsafe 242 def unwrap_unchecked(self) -> T: 243 """Returns the contained Some value without checking that the value is not None. 244 245 Example 246 ------- 247 ```py 248 v: Option[float] = Some(1.2) 249 v.unwrap_unchecked() # 1.2 250 251 v: Option[float] = Some(None) 252 print(v.unwrap_unchecked()) # Undefined Behavior 253 ``` 254 """ 255 #! SAFETY: The caller guarantees that the value is not None. 256 return self._value # pyright: ignore
Returns the contained Some value without checking that the value is not None.
Example
v: Option[float] = Some(1.2)
v.unwrap_unchecked() # 1.2
v: Option[float] = Some(None)
print(v.unwrap_unchecked()) # Undefined Behavior
Safety ⚠️
Calling this method without knowing its output is considered undefined behavior.
258 def expect(self, message: str, /) -> T: 259 """Returns the contained `Some` value. 260 261 raises if the value is `None` with a custom provided `message`. 262 263 Example 264 ------- 265 ```py 266 value = Some("Hello") 267 268 print(value.expect("Value is None")) 269 # "Hello" 270 271 value: Option[str] = Some(None) 272 print(value.expect("Value is None")) 273 # RuntimeError("Value is None") 274 ``` 275 """ 276 if self._value is None: 277 raise RuntimeError(message) 278 279 return self._value
Returns the contained Some
value.
raises if the value is None
with a custom provided message
.
Example
value = Some("Hello")
print(value.expect("Value is None"))
# "Hello"
value: Option[str] = Some(None)
print(value.expect("Value is None"))
# RuntimeError("Value is None")
283 def map(self, f: Fn[T, U], /) -> Option[U]: 284 """Map the inner value to another type. Returning `Some(None)` if `T` is `None`. 285 286 Example 287 ------- 288 ```py 289 value = Some(5.0) 290 291 print(value.map(lambda x: x * 2.0)) 292 # Some(10.0) 293 294 value: Option[bool] = Some(None) 295 print(value) 296 # Some(None) 297 ``` 298 """ 299 if self._value is None: 300 return NOTHING 301 302 return Some(f(self._value))
Map the inner value to another type. Returning Some(None)
if T
is None
.
Example
value = Some(5.0)
print(value.map(lambda x: x * 2.0))
# Some(10.0)
value: Option[bool] = Some(None)
print(value)
# Some(None)
304 def map_or(self, default: U, f: Fn[T, U], /) -> U: 305 """Map the inner value to another type or return `default` if its `None`. 306 307 Example 308 ------- 309 ```py 310 value: Option[float] = Some(5.0) 311 312 # map to int. 313 print(value.map_or(0, int)) 314 # 6 315 316 value: Option[float] = Some(None) 317 print(value.map_or(0, int) 318 # 0 319 ``` 320 """ 321 if self._value is None: 322 return default 323 324 return f(self._value)
Map the inner value to another type or return default
if its None
.
Example
value: Option[float] = Some(5.0)
# map to int.
print(value.map_or(0, int))
# 6
value: Option[float] = Some(None)
print(value.map_or(0, int)
# 0
326 def map_or_else(self, default: FnOnce[U], f: Fn[T, U], /) -> U: 327 """Map the inner value to another type, or return `default()` if its `None`. 328 329 Example 330 ------- 331 ```py 332 def default() -> int: 333 return sys.getsizeof(object()) 334 335 value: Option[float] = Some(5.0) 336 337 # map to int. 338 print(value.map_or_else(default, int)) 339 # 6 340 341 value: Option[float] = Some(None) 342 print(value.map_or_else(default, int) 343 # 28 <- size of object() 344 ``` 345 """ 346 if self._value is None: 347 return default() 348 349 return f(self._value)
Map the inner value to another type, or return default()
if its None
.
Example
def default() -> int:
return sys.getsizeof(object())
value: Option[float] = Some(5.0)
# map to int.
print(value.map_or_else(default, int))
# 6
value: Option[float] = Some(None)
print(value.map_or_else(default, int)
# 28 <- size of object()
351 def filter(self, predicate: Fn[T, bool]) -> Option[T]: 352 """Returns `Some(None)` if the contained value is `None`, 353 354 otherwise calls the predicate and returns `Some(T)` if the predicate returns `True`. 355 356 Example 357 ------- 358 ```py 359 value = Some([1, 2, 3]) 360 361 print(value.filter(lambda x: 1 in x)) 362 # Some([1, 2, 3]) 363 364 value: Option[int] = Some([1, 2, 3]) # or Some(None) 365 print(value.filter(lambda x: 1 not in x)) 366 # None 367 ``` 368 """ 369 if (value := self._value) is not None: 370 if predicate(value): 371 return Some(value) 372 373 return NOTHING
Returns Some(None)
if the contained value is None
,
otherwise calls the predicate and returns Some(T)
if the predicate returns True
.
Example
value = Some([1, 2, 3])
print(value.filter(lambda x: 1 in x))
# Some([1, 2, 3])
value: Option[int] = Some([1, 2, 3]) # or Some(None)
print(value.filter(lambda x: 1 not in x))
# None
375 def ok_or(self, err: U) -> _result.Result[T, U]: 376 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err)`. 377 378 Example 379 ------- 380 ```py 381 xyz: Option[str] = Some("foo") 382 assert xyz.ok_or(None) == Ok("foo") 383 384 xyz: Option[str] = Some(None) 385 assert xyz.ok_or(None) == Err(None) 386 ``` 387 """ 388 if self._value is None: 389 return _result.Err(err) 390 391 return _result.Ok(self._value)
Transforms the Option<T>
into a Result<T, E>
, mapping Some(v)
to Ok(v)
and None
to Err(err)
.
Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")
xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
393 def ok_or_else(self, err: FnOnce[U]) -> _result.Result[T, U]: 394 """Transforms the `Option<T>` into a `Result<T, E>`, mapping `Some(v)` to `Ok(v)` and `None` to `Err(err())`. 395 396 Example 397 ------- 398 ```py 399 xyz: Option[str] = Some("foo") 400 assert xyz.ok_or(None) == Ok("foo") 401 402 xyz: Option[str] = Some(None) 403 assert xyz.ok_or(None) == Err(None) 404 ``` 405 """ 406 if self._value is None: 407 return _result.Err(err()) 408 409 return _result.Ok(self._value)
Transforms the Option<T>
into a Result<T, E>
, mapping Some(v)
to Ok(v)
and None
to Err(err())
.
Example
xyz: Option[str] = Some("foo")
assert xyz.ok_or(None) == Ok("foo")
xyz: Option[str] = Some(None)
assert xyz.ok_or(None) == Err(None)
411 def zip(self, other: Option[U]) -> Option[tuple[T, U]]: 412 """Zips `self` with `other`. 413 414 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some((s, o))` otherwise `None`. 415 416 Example 417 ------- 418 ```py 419 x = Some(1) 420 y = Some("hi") 421 z: Option[str] = Some(None) 422 423 assert x.zip(y) == Some((1, "hi")) 424 assert x.zip(z) == Some(None) 425 ``` 426 """ 427 if self._value is not None and other._value is not None: 428 return Some((self._value, other._value)) 429 430 return NOTHING
Zips self
with other
.
if self
is Some(s)
and other is Some(o)
, this returns Some((s, o))
otherwise None
.
Example
x = Some(1)
y = Some("hi")
z: Option[str] = Some(None)
assert x.zip(y) == Some((1, "hi"))
assert x.zip(z) == Some(None)
432 def zip_with( 433 self, other: Option[U], f: collections.Callable[[T, U], T_co] 434 ) -> Option[T_co]: 435 """Zips `self` with `other` using function `f`. 436 437 if `self` is `Some(s)` and other is `Some(o)`, this returns `Some(f(s, o))` otherwise `None`. 438 439 Example 440 ------- 441 ```py 442 @dataclass 443 class Point: 444 x: float 445 y: float 446 447 x, y = Some(32.1), Some(42.4) 448 assert x.zip_with(y, Point) == Some(Point(32.1, 42.4)) 449 ``` 450 """ 451 if self._value is not None and other._value is not None: 452 return Some(f(self._value, other._value)) 453 454 return NOTHING
Zips self
with other
using function f
.
if self
is Some(s)
and other is Some(o)
, this returns Some(f(s, o))
otherwise None
.
Example
@dataclass
class Point:
x: float
y: float
x, y = Some(32.1), Some(42.4)
assert x.zip_with(y, Point) == Some(Point(32.1, 42.4))
458 def take(self) -> Option[T]: 459 """Take the value from `self` Setting it to `None`, and then return `Some(v)`. 460 461 If you don't care about the original value, use `Option.clear()` instead. 462 463 Example 464 ------- 465 ```py 466 original = Some("Hi") 467 new = original.take() 468 469 print(original, new) 470 # None, Some("Hi") 471 ``` 472 """ 473 if self._value is None: 474 return NOTHING 475 476 val = self._value 477 self._value = None 478 return Some(val)
Take the value from self
Setting it to None
, and then return Some(v)
.
If you don't care about the original value, use Option.clear()
instead.
Example
original = Some("Hi")
new = original.take()
print(original, new)
# None, Some("Hi")
480 def take_if(self, predicate: collections.Callable[[T], bool]) -> Option[T]: 481 """Take the value from `Self`, Setting it to `None` only if predicate returns `True`. 482 483 If you don't care about the original value, use `Option.clear_if()` instead. 484 485 Example 486 ------- 487 ```py 488 def validate(email: str) -> bool: 489 # you can obviously validate this better. 490 return email.find('@') == 1 491 492 original = Some("flex@gg.com") 493 valid = original.take_if(validate) 494 assert is_allowed.is_some() and original.is_none() 495 496 original = Some("mail.example.com") 497 invalid = original.take_if(validate) 498 assert invalid.is_none() and original.is_some() 499 ``` 500 """ 501 if self.map_or(False, predicate): 502 return self.take() 503 504 return NOTHING
Take the value from Self
, Setting it to None
only if predicate returns True
.
If you don't care about the original value, use Option.clear_if()
instead.
Example
def validate(email: str) -> bool:
# you can obviously validate this better.
return email.find('@') == 1
original = Some("flex@gg.com")
valid = original.take_if(validate)
assert is_allowed.is_some() and original.is_none()
original = Some("mail.example.com")
invalid = original.take_if(validate)
assert invalid.is_none() and original.is_some()
506 def clear(self) -> None: 507 """Clear the inner value, setting it to `None`. 508 509 If you care about the original value, use `Option.take()` instead. 510 511 Example 512 ------- 513 ```py 514 value = Some("Hello") 515 value.clear() 516 assert value.is_none() 517 ``` 518 """ 519 self._value = None
Clear the inner value, setting it to None
.
If you care about the original value, use Option.take()
instead.
Example
value = Some("Hello")
value.clear()
assert value.is_none()
521 def clear_if(self, predicate: Fn[T, bool]) -> None: 522 """Clear the inner value, setting it to `None` if the predicate returns `True`. 523 524 If you care about the original value, use `Option.take_if()` instead. 525 526 Example 527 ------- 528 ```py 529 value = Some("Hello") 530 value.clear_if(lambda x: x == "Hello") 531 assert value.is_none() 532 ``` 533 """ 534 if self._value is not None and predicate(self._value): 535 self._value = None
Clear the inner value, setting it to None
if the predicate returns True
.
If you care about the original value, use Option.take_if()
instead.
Example
value = Some("Hello")
value.clear_if(lambda x: x == "Hello")
assert value.is_none()
537 def replace(self, value: T) -> Option[T]: 538 """Replace the contained value with another value. 539 540 Use `Option.insert` if you want to return the original value 541 that got inserted instead of `self` 542 543 Example 544 ------- 545 ```py 546 value: Option[str] = Some(None) 547 value.replace("Hello") 548 # Some("Hello") 549 ``` 550 """ 551 self._value = value 552 return self
Replace the contained value with another value.
Use Option.insert
if you want to return the original value
that got inserted instead of self
Example
value: Option[str] = Some(None)
value.replace("Hello")
# Some("Hello")
554 def insert(self, value: T) -> T: 555 """Insert a value into the option, and then return a reference to it. 556 557 This will overwrite the old value if it was already contained. 558 559 Example 560 ------- 561 ```py 562 flag: Option[bool] = Some(None) 563 flag_ref = flag.insert(True) 564 assert flag_ref == True 565 assert flag.unwrap() == True 566 ``` 567 """ 568 self._value = value 569 return value
Insert a value into the option, and then return a reference to it.
This will overwrite the old value if it was already contained.
Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
571 def get_or_insert(self, value: T) -> T: 572 """Insert a value into the option if it was `None`, 573 and then return a reference to it. 574 575 Example 576 ------- 577 ```py 578 state: Option[bool] = Some(None) 579 assert state.get_or_insert(True) is True 580 assert state.get_or_insert(False) is True 581 ``` 582 """ 583 if self._value is not None: 584 return self._value 585 586 self._value = value 587 return value
Insert a value into the option if it was None
,
and then return a reference to it.
Example
state: Option[bool] = Some(None)
assert state.get_or_insert(True) is True
assert state.get_or_insert(False) is True
589 def get_or_insert_with(self, f: FnOnce[T]) -> T: 590 """Insert a value into the option computed from `f()` if it was `None`, 591 and then return a reference to it. 592 593 Example 594 ------- 595 ```py 596 flag: Option[bool] = Some(None) 597 flag_ref = flag.insert(True) 598 assert flag_ref == True 599 assert flag.unwrap() == True 600 ``` 601 """ 602 if self._value is not None: 603 return self._value 604 605 v = self._value = f() 606 return v
Insert a value into the option computed from f()
if it was None
,
and then return a reference to it.
Example
flag: Option[bool] = Some(None)
flag_ref = flag.insert(True)
assert flag_ref == True
assert flag.unwrap() == True
608 def and_ok(self, optb: Option[T]) -> Option[T]: 609 """Returns `None` if `self` or `optb` is `None`, otherwise return `optb`. 610 611 aliases: `Option::and` 612 613 Example 614 ------- 615 ```py 616 x = Some(1) 617 y: Option[str] = Some(None) 618 assert x.and_ok(y) == Some(None) 619 620 x: Option[str] = Some(None) 621 y = Some(1) 622 assert x.and_ok(y) == Some(None) 623 624 x: Option[str] = Some("hi") 625 y = Some(100) 626 assert x.and_ok(y) == Some(100) 627 ``` 628 """ 629 if self._value is None or optb._value is None: 630 return optb 631 632 return NOTHING
Returns None
if self
or optb
is None
, otherwise return optb
.
aliases: Option::and
Example
x = Some(1)
y: Option[str] = Some(None)
assert x.and_ok(y) == Some(None)
x: Option[str] = Some(None)
y = Some(1)
assert x.and_ok(y) == Some(None)
x: Option[str] = Some("hi")
y = Some(100)
assert x.and_ok(y) == Some(100)
634 def and_then(self, f: Fn[T, Option[T]]) -> Option[T]: 635 """Returns `Some(None)` if the contained value is `None`, otherwise call `f()` 636 on `T` and return `Option[T]`. 637 638 Example 639 ------- 640 ```py 641 value = Some(5) 642 print(value.and_then(lambda x: Some(x * 2))) 643 # Some(10) 644 645 value: Option[int] = Some(None) 646 print(value.and_then(lambda x: Some(x * 2))) 647 # Some(None) 648 ``` 649 """ 650 if self._value is None: 651 return NOTHING 652 653 return f(self._value)
Returns Some(None)
if the contained value is None
, otherwise call f()
on T
and return Option[T]
.
Example
value = Some(5)
print(value.and_then(lambda x: Some(x * 2)))
# Some(10)
value: Option[int] = Some(None)
print(value.and_then(lambda x: Some(x * 2)))
# Some(None)
655 def inspect(self, f: Fn[T, typing.Any]) -> Option[T]: 656 """Calls `f()` on the contained value if it was `Some(v)`, otherwise does nothing. 657 658 Example 659 ------- 660 ```py 661 def debug(x: str) -> None: 662 print("Debugging:", x) 663 664 value = Some("foo") 665 inner = value.inspect(debug).expect("no value to debug") 666 # prints: Debugging: "foo" 667 668 value: Option[str] = Some(None) 669 value.inspect(debug) # prints nothing 670 """ 671 if self._value is not None: 672 f(self._value) 673 674 return self
Calls f()
on the contained value if it was Some(v)
, otherwise does nothing.
Example
```py def debug(x: str) -> None: print("Debugging:", x)
value = Some("foo") inner = value.inspect(debug).expect("no value to debug")
prints: Debugging: "foo"
value: Option[str] = Some(None) value.inspect(debug) # prints nothing
678 def iter(self) -> _iter.ExactSizeIterator[T]: 679 """Returns an iterator over the contained value. 680 681 Example 682 ------- 683 ```py 684 from sain import Some 685 value = Some("gg").iter() 686 assert value.next() == Some("gg") 687 688 value: Option[int] = Some(None) 689 assert value.iter().next().is_none() 690 ``` 691 """ 692 if self._value is None: 693 return _iter.empty() 694 695 return _iter.once(self._value)
Returns an iterator over the contained value.
Example
from sain import Some
value = Some("gg")sain.iter()
assert value.next() == Some("gg")
value: Option[int] = Some(None)
assert value.iter().next().is_none()
699 def is_some(self) -> bool: 700 """Returns `True` if the contained value is not `None`, otherwise returns `False`. 701 702 Example 703 ------- 704 ```py 705 value = Some(5) 706 print(value.is_some()) 707 # True 708 709 value: Option[int] = Some(None) 710 print(value.is_some()) 711 # False 712 ``` 713 """ 714 return self._value is not None
Returns True
if the contained value is not None
, otherwise returns False
.
Example
value = Some(5)
print(value.is_some())
# True
value: Option[int] = Some(None)
print(value.is_some())
# False
716 def is_some_and(self, predicate: Fn[T, bool]) -> bool: 717 """Returns `True` if the contained value is not `None` and 718 the predicate returns `True`, otherwise returns `False`. 719 720 Example 721 ------- 722 ```py 723 value = Some(5) 724 print(value.is_some_and(lambda x: x > 3)) 725 # True 726 727 value: Option[int] = Some(None) 728 print(value.is_some_and(lambda x: x > 3)) 729 # False 730 ``` 731 """ 732 return self._value is not None and predicate(self._value)
Returns True
if the contained value is not None
and
the predicate returns True
, otherwise returns False
.
Example
value = Some(5)
print(value.is_some_and(lambda x: x > 3))
# True
value: Option[int] = Some(None)
print(value.is_some_and(lambda x: x > 3))
# False
734 def is_none(self) -> bool: 735 """Returns `True` if the contained value is `None`, otherwise returns `False`. 736 737 Example 738 ------- 739 ```py 740 value = Some(5) 741 print(value.is_none()) 742 # False 743 744 value: Option[int] = Some(None) 745 print(value.is_none()) 746 # True 747 ``` 748 """ 749 return self._value is None
Returns True
if the contained value is None
, otherwise returns False
.
Example
value = Some(5)
print(value.is_none())
# False
value: Option[int] = Some(None)
print(value.is_none())
# True
751 def is_none_or(self, f: Fn[T, bool]) -> bool: 752 """Returns `True` if the contained value is `None` or the predicate returns `True`, 753 otherwise returns `False`. 754 755 Example 756 ------- 757 ```py 758 value = Some(5) 759 print(value.is_none_or(lambda x: x > 3)) 760 # False 761 762 value: Option[int] = Some(None) 763 print(value.is_none_or(lambda x: x > 3)) 764 # True 765 ``` 766 """ 767 match self._value: 768 case None: 769 return True 770 case x: 771 return f(x)
Returns True
if the contained value is None
or the predicate returns True
,
otherwise returns False
.
Example
value = Some(5)
print(value.is_none_or(lambda x: x > 3))
# False
value: Option[int] = Some(None)
print(value.is_none_or(lambda x: x > 3))
# True
1041@rustc_diagnostic_item("Iter") 1042@typing.final 1043@diagnostic 1044class Iter(typing.Generic[Item], Iterator[Item]): 1045 """a lazy iterator that has its items ready in-memory. 1046 1047 This is similar to Rust `std::slice::Iter<T>` item which iterables can build 1048 from this via `.iter()` method. 1049 1050 Example 1051 ------- 1052 ```py 1053 iterator = Iter([1, 2, 3]) 1054 1055 # Limit the results to 2. 1056 for item in iterator.take(2): 1057 print(item) 1058 # 1 1059 # 2 1060 1061 # Filter the results. 1062 for item in iterator.filter(lambda item: item > 1): 1063 print(item) 1064 # 2 1065 # 3 1066 # 3 1067 1068 # Indexing is supported. 1069 print(iterator[0]) 1070 # 1 1071 ``` 1072 1073 Parameters 1074 ---------- 1075 items: `Iterable[Item]` 1076 The items to iterate over. This can be anything that implements `__iter__` and `__next__`. 1077 """ 1078 1079 __slots__ = ("_it",) 1080 1081 def __init__(self, iterable: collections.Iterable[Item]) -> None: 1082 self._it = iter(iterable) 1083 1084 def clone(self) -> Iter[Item]: 1085 """Return a copy of this iterator. 1086 1087 ```py 1088 it = Iter([1, 2, 3]) 1089 1090 for i in it.clone(): 1091 ... 1092 1093 # The actual iterator hasn't been exhausted. 1094 assert it.count() == 3 1095 ``` 1096 """ 1097 return Iter(copy.copy(self._it)) 1098 1099 def __next__(self) -> Item: 1100 return next(self._it) 1101 1102 def __getitem__(self, index: int) -> Item: 1103 return self.skip(index).first().unwrap_or_else(oob) 1104 1105 def __contains__(self, item: Item) -> bool: 1106 return item in self._it
a lazy iterator that has its items ready in-memory.
This is similar to Rust std::slice::Iter<T>
item which iterables can build
from this via .iter()
method.
Example
iterator = Iter([1, 2, 3])
# Limit the results to 2.
for item in iterator.take(2):
print(item)
# 1
# 2
# Filter the results.
for item in iterator.filter(lambda item: item > 1):
print(item)
# 2
# 3
# 3
# Indexing is supported.
print(iterator[0])
# 1
Parameters
- items (
Iterable[Item]
): The items to iterate over. This can be anything that implements__iter__
and__next__
.
Implementations
This class implements Iter in Rust.
1084 def clone(self) -> Iter[Item]: 1085 """Return a copy of this iterator. 1086 1087 ```py 1088 it = Iter([1, 2, 3]) 1089 1090 for i in it.clone(): 1091 ... 1092 1093 # The actual iterator hasn't been exhausted. 1094 assert it.count() == 3 1095 ``` 1096 """ 1097 return Iter(copy.copy(self._it))
Return a copy of this iterator.
it = Iter([1, 2, 3])
for i in it.clone():
...
# The actual iterator hasn't been exhausted.
assert it.count() == 3
123@rustc_diagnostic_item("Iterator") 124class Iterator( 125 typing.Generic[Item], 126 abc.ABC, 127 _default.Default["Empty[Item]"], 128): 129 """An abstract interface for dealing with iterators. 130 131 This is exactly the same trait as `core::iter::Iterator` trait from Rust. 132 133 This is the main interface that any type can implement by basically inheriting from it. 134 The method `__next__` is the only method that needs to be implemented, You get all the other methods for free. 135 136 If you want to use a ready iterator for general purposes, Use `Iter` or `TrustedIter`. This interface is only for implementers 137 and type hints. 138 139 Example 140 ------- 141 ```py 142 @dataclass 143 class Counter(Iterator[int]): 144 start: int = 0 145 stop: int | None = None 146 147 # implement the required method. 148 def __next__(self) -> int: 149 result = self.start 150 self.start += 1 151 152 if self.stop is not None and result >= self.stop: 153 raise StopIteration 154 155 return result 156 157 counter = Counter(start=0, stop=10) 158 for i in counter.map(lambda x: x * 2): # multiply each number 159 ... 160 ``` 161 """ 162 163 __slots__ = () 164 165 ##################### 166 # Required function # 167 ##################### 168 169 @abc.abstractmethod 170 def __next__(self) -> Item: 171 raise NotImplementedError 172 173 ###################### 174 # provided functions # 175 ###################### 176 177 @staticmethod 178 @typing.final 179 def default() -> Empty[Item]: 180 """Return the default iterator for this type. It returns an empty iterator. 181 182 Example 183 ------- 184 ```py 185 it: Iterator[int] = Iter.default() 186 assert t.next().is_none() 187 ``` 188 """ 189 return Empty() 190 191 @typing.overload 192 def collect(self) -> collections.MutableSequence[Item]: ... 193 194 @typing.overload 195 def collect( 196 self, *, cast: collections.Callable[[Item], OtherItem] 197 ) -> collections.MutableSequence[OtherItem]: ... 198 199 @typing.final 200 def collect( 201 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 202 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 203 """Collects all items in the iterator into a sequence. 204 205 Example 206 ------- 207 ```py 208 iterator = Iter(range(3)) 209 iterator.collect() 210 # [0, 1, 2, 3] 211 iterator.collect(cast=str) # Map each element and collect it. 212 # ['0', '1', '2', '3'] 213 ``` 214 215 Parameters 216 ---------- 217 cast: `T | None` 218 An optional type to cast the items into. 219 If not provided the items will be returned as it's original type. 220 """ 221 if cast is not None: 222 return [cast(i) for i in self] 223 224 return [_ for _ in self] 225 226 @typing.final 227 def collect_into(self, collection: Collector[Item]) -> None: 228 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 229 230 if `collection` is a `MutableSequence[Item]`, this iterator will call `extend` on it, 231 and if it was a `set[Item]`, this will call `update` on it. 232 233 Example 234 ------- 235 ```py 236 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 237 uniques = set() 238 iterator.collect_into(uniques) 239 # assert uniques == {1, 2, 3, 4, 6} 240 ``` 241 242 Parameters 243 ---------- 244 collection: `MutableSequence[T]` | `set[T]` 245 The collection to extend the items in this iterator with. 246 """ 247 if isinstance(collection, collections.MutableSequence): 248 collection.extend(_ for _ in self) 249 else: 250 collection.update(_ for _ in self) 251 252 @typing.final 253 def to_vec(self) -> Vec[Item]: 254 """Consume this iterator, returning all of its elements in a `Vec[T]`. 255 256 Example 257 ------- 258 ```py 259 it = sain.iter.once(0) 260 vc = it.to_vec() 261 262 assert to_vec == [0] 263 ``` 264 """ 265 from sain.collections.vec import Vec 266 267 return Vec(_ for _ in self) 268 269 @typing.final 270 def sink(self) -> None: 271 """Consume all elements from this iterator, returning nothing. 272 273 Example 274 ------- 275 ```py 276 it = Iter((1, 2, 3)) 277 it.sink() 278 assert it.next().is_none() 279 ``` 280 """ 281 for _ in self: 282 pass 283 284 @typing.final 285 def raw_parts(self) -> collections.Generator[Item, None, None]: 286 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 287 288 ```py 289 it = Iter("cba") 290 sort = sorted(it.raw_parts()) 291 292 assert it.count() == 0 293 assert sort == ["a", "b", "c"] 294 ``` 295 """ 296 for item in self: 297 yield item 298 299 def next(self) -> Option[Item]: 300 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 301 302 Example 303 ------- 304 ```py 305 iterator = Iter(["1", "2"]) 306 assert iterator.next() == Some("1") 307 assert iterator.next() == Some("2") 308 assert iterator.next().is_none() 309 ``` 310 """ 311 try: 312 return _option.Some(self.__next__()) 313 except StopIteration: 314 # SAFETY: No more items in the iterator. 315 return _option.NOTHING 316 317 def cloned(self) -> Cloned[Item]: 318 """Creates an iterator which shallow copies its elements by reference. 319 320 .. note:: 321 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 322 on each item that is being yielded. 323 324 Example 325 ------- 326 ```py 327 @dataclass 328 class User: 329 users_ids: list[int] = [] 330 331 # An iterator which elements points to the same user. 332 user = User() 333 it = Iter((user, user)) 334 335 for u in it.cloned(): 336 u.user_ids.append(1) 337 338 # We iterated over the same user pointer twice and appended "1" 339 # since `copy` returns a shallow copy of nested structures. 340 assert len(user.user_ids) == 2 341 ``` 342 """ 343 return Cloned(self) 344 345 def copied(self) -> Copied[Item]: 346 """Creates an iterator which copies all of its elements by value. 347 348 If you only need a copy of the item reference, Use `.cloned()` instead. 349 350 .. note:: 351 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 352 on each item that is being yielded. 353 354 Example 355 ------- 356 ```py 357 @dataclass 358 class User: 359 users_ids: list[int] = [] 360 361 # An iterator which elements points to the same user. 362 user = User() 363 it = Iter((user, user)) 364 365 for u in it.copied(): 366 # A new list is created for each item. 367 u.user_ids.append(1) 368 369 # The actual list is untouched since we consumed a deep copy of it. 370 assert len(user.user_ids) == 0 371 ``` 372 """ 373 return Copied(self) 374 375 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 376 """Maps each item in the iterator to another type. 377 378 Example 379 ------- 380 ```py 381 iterator = Iter(["1", "2", "3"]).map(int) 382 383 for item in iterator: 384 assert isinstance(item, int) 385 ``` 386 387 Parameters 388 ---------- 389 predicate: `Callable[[Item], OtherItem]` 390 The function to map each item in the iterator to the other type. 391 """ 392 return Map(self, fn) 393 394 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 395 """Filters the iterator to only yield items that match the predicate. 396 397 Example 398 ------- 399 ```py 400 places = Iter(['London', 'Paris', 'Los Angeles']) 401 for place in places.filter(lambda place: place.startswith('L')): 402 print(place) 403 404 # London 405 # Los Angeles 406 ``` 407 """ 408 return Filter(self, predicate) 409 410 def take(self, count: int) -> Take[Item]: 411 """Take the first number of items until the number of items 412 are yielded or the end of the iterator is exhausted. 413 414 Example 415 ------- 416 ```py 417 iterator = Iter(['c', 'x', 'y']) 418 419 for x in iterator.take(2): 420 assert x in ('c', 'x') 421 422 # <Iter(['c', 'x'])> 423 ``` 424 """ 425 return Take(self, count) 426 427 def skip(self, count: int) -> Skip[Item]: 428 """Skips the first number of items in the iterator. 429 430 Example 431 ------- 432 ```py 433 iterator = Iter((1, 2, 3, 4)) 434 for i in iterator.skip(2): 435 print(i) 436 437 # 3 438 # 4 439 ``` 440 """ 441 return Skip(self, count) 442 443 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 444 """Create a new iterator that yields a tuple of the index and item. 445 446 Example 447 ------- 448 ```py 449 iterator = Iter([1, 2, 3]) 450 for index, item in iterator.enumerate(): 451 print(index, item) 452 453 # 0 1 454 # 1 2 455 # 2 3 456 ``` 457 """ 458 return Enumerate(self, start) 459 460 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 461 """yields items from the iterator while predicate returns `True`. 462 463 The rest of the items are discarded as soon as the predicate returns `False` 464 465 Example 466 ------- 467 ```py 468 iterator = Iter(['a', 'ab', 'xd', 'ba']) 469 for x in iterator.take_while(lambda x: 'a' in x): 470 print(x) 471 472 # a 473 # ab 474 ``` 475 476 Parameters 477 ---------- 478 predicate: `collections.Callable[[Item], bool]` 479 The function to predicate each item in the iterator. 480 """ 481 return TakeWhile(self, f) 482 483 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 484 """Yields items from the iterator while predicate returns `False`. 485 486 Example 487 ------- 488 ```py 489 iterator = Iter(['a', 'ab', 'xd', 'ba']) 490 for x in iterator.drop_while(lambda x: 'a' in x): 491 print(x) 492 493 # xd 494 # ba 495 ``` 496 497 Parameters 498 ---------- 499 predicate: `collections.Callable[[Item], bool]` 500 The function to predicate each item in the iterator. 501 """ 502 return DropWhile(self, f) 503 504 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 505 """Returns an iterator over `chunk_size` elements of the iterator at a time, 506 starting at the beginning of the iterator. 507 508 Example 509 ------- 510 ```py 511 iter = Iter(['a', 'b', 'c', 'd', 'e']) 512 chunks = iter.chunks() 513 assert chunks.next().unwrap() == ['a', 'b'] 514 assert chunks.next().unwrap() == ['c', 'd'] 515 assert chunks.next().unwrap() == ['e'] 516 assert chunks.next().is_none() 517 ``` 518 """ 519 return Chunks(self, chunk_size) 520 521 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 522 """Return `True` if all items in the iterator match the predicate. 523 524 Example 525 ------- 526 ```py 527 iterator = Iter([1, 2, 3]) 528 if iterator.all(lambda item: isinstance(item, int)): 529 print("yes") 530 ``` 531 532 Parameters 533 ---------- 534 predicate: `collections.Callable[[Item], bool]` 535 The function to test each item in the iterator. 536 """ 537 return all(predicate(item) for item in self) 538 539 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 540 """`True` if any items in the iterator match the predicate. 541 542 Example 543 ------- 544 ```py 545 iterator = Iter([1, 2, 3]) 546 if iterator.any(lambda item: isinstance(item, int)): 547 print("At least one item is an int.") 548 # At least one item is an int. 549 ``` 550 551 Parameters 552 ---------- 553 predicate: `collections.Callable[[Item], bool]` 554 The function to test each item in the iterator. 555 """ 556 return any(predicate(item) for item in self) 557 558 def zip( 559 self, other: collections.Iterable[OtherItem] 560 ) -> Iter[tuple[Item, OtherItem]]: 561 """Zips the iterator with another iterable. 562 563 Example 564 ------- 565 ```py 566 xs = Iter([1, 2, 3]) 567 ys = [4, 5, 6] 568 569 iter = xs.zip(ys) 570 571 assert iter.next().unwrap() == (1, 4) 572 assert iter.next().unwrap() == (2, 5) 573 assert iter.next().unwrap() == (3, 6) 574 ``` 575 576 Parameters 577 ---------- 578 other: `Iterable[OtherItem]` 579 The iterable to zip with. 580 """ 581 return Iter(zip(self.raw_parts(), other)) 582 583 def sort( 584 self, 585 *, 586 key: collections.Callable[[Item], SupportsRichComparison] | None = None, 587 reverse: bool = False, 588 ) -> collections.MutableSequence[Item]: 589 """Sorts the iterator elements and return it in a mutable sequence. 590 591 Example 592 ------- 593 ```py 594 iterator = Iter([3, 1, 6, 7]) 595 for item in iterator.sort(key=lambda item: item < 3): 596 print(item) 597 # 1 598 # 3 599 # 6 600 # 7 601 ``` 602 603 Parameters 604 ---------- 605 key: `collections.Callable[[Item], Any]` 606 The function to sort by. 607 reverse: `bool` 608 Whether to reverse the sort. 609 """ 610 return sorted([_ for _ in self], key=key, reverse=reverse) # pyright: ignore - key can be None here 611 612 def reversed(self) -> Iter[Item]: 613 """Returns a new iterator that yields the items in the iterator in reverse order. 614 615 This consumes this iterator into a sequence and return a new iterator containing all of the elements 616 in reversed order. 617 618 Example 619 ------- 620 ```py 621 iterator = Iter([3, 1, 6, 7]) 622 for item in iterator.reversed(): 623 print(item) 624 # 7 625 # 6 626 # 1 627 # 3 628 ``` 629 """ 630 # NOTE: In order to reverse the iterator we need to 631 # first collect it into some collection. 632 return Iter(reversed([_ for _ in self])) 633 634 def union(self, other: collections.Iterable[Item]) -> Iter[Item]: 635 """Returns a new iterator that yields all items from both iterators. 636 637 Example 638 ------- 639 ```py 640 iterator = Iter([1, 2, 3]) 641 other = [4, 5, 6] 642 643 for item in iterator.union(other): 644 print(item) 645 # 1 646 # 2 647 # 3 648 # 4 649 # 5 650 # 6 651 ``` 652 653 Parameters 654 ---------- 655 other: `Iter[Item]` 656 The iterable to union with. 657 """ 658 return Iter(itertools.chain(self.raw_parts(), other)) 659 660 def first(self) -> Option[Item]: 661 """Returns the first item in the iterator. 662 663 Example 664 ------- 665 ```py 666 iterator = Iter([3, 1, 6, 7]) 667 iterator.first().is_some_and(lambda x: x == 3) 668 ``` 669 """ 670 return self.take(1).next() 671 672 def last(self) -> Option[Item]: 673 """Returns the last item in the iterator. 674 675 Example 676 ------- 677 ```py 678 iterator = Iter([3, 1, 6, 7]) 679 iterator.last().is_some_and(lambda x: x == 7) 680 ``` 681 """ 682 return self.reversed().first() 683 684 def count(self) -> int: 685 """Consume this iterator, returning the count of elements it has. 686 687 Example 688 ------- 689 ```py 690 it = Iter(range(3)) 691 assert it.count() == 3 692 ``` 693 """ 694 count = 0 695 for _ in self: 696 count += 1 697 698 return count 699 700 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 701 """Searches for an element of an iterator that satisfies a predicate. 702 703 If you want the position of the element, use `Iterator.position` instead. 704 705 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 706 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 707 708 Example 709 ------- 710 ```py 711 it = Iter(range(10)) 712 item = it.find(lambda num: num > 5) 713 print(item) # 6 714 ``` 715 """ 716 for item in self: 717 if predicate(item): 718 return _option.Some(item) 719 720 # no more items 721 return _option.NOTHING 722 723 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 724 """Searches for the position of an element in the iterator that satisfies a predicate. 725 726 If you want the object itself, use `Iterator.find` instead. 727 728 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 729 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 730 731 Example 732 ------- 733 ```py 734 it = Iter(range(10)) 735 position = it.find(lambda num: num > 5) 736 assert position.unwrap() == 6 737 ``` 738 """ 739 for position, value in self.enumerate(): 740 if predicate(value): 741 return _option.Some(position) 742 743 # no more items 744 return _option.NOTHING 745 746 def fold( 747 self, init: OtherItem, f: collections.Callable[[OtherItem, Item], OtherItem] 748 ) -> OtherItem: 749 """Folds every element into an accumulator by applying an operation, returning the final result. 750 751 fold() takes two arguments: an initial value, and a closure with two arguments: an ‘accumulator’, and an element. 752 The closure returns the value that the accumulator should have for the next iteration. 753 754 The initial value is the value the accumulator will have on the first call. 755 756 After applying this closure to every element of the iterator, fold() returns the accumulator. 757 758 This operation is sometimes called ‘reduce’ or ‘inject’. 759 760 Example 761 ------- 762 ```py 763 a = Iter([1, 2, 3, 4]) 764 sum = a.fold(0, lambda acc, elem: acc + elem) 765 assert sum == 10 766 ``` 767 """ 768 accum = init 769 while True: 770 try: 771 x = self.__next__() 772 accum = f(accum, x) 773 except StopIteration: 774 break 775 776 return accum 777 778 def advance_by(self, n: int) -> _result.Result[None, int]: 779 """Advances the iterator by `n` elements. 780 781 Returns `Result[None, int]`, where `Ok(None)` means the iterator 782 advanced successfully, and `Err(int)` if `None` encountered, where `int` 783 represents the remaining number of steps that could not be advanced because the iterator ran out. 784 785 Example 786 ------- 787 ```py 788 it = into_iter([1, 2, 3, 4]) 789 assert it.advance_by(2).is_ok() 790 assert it.next() == Some(3) 791 assert it.advance_by(0).is_ok() 792 assert it.advance_by(100) == Err(99) 793 ``` 794 """ 795 for i in range(n): 796 try: 797 self.__next__() 798 except StopIteration: 799 return _result.Err(n - i) 800 801 return _result.Ok(None) 802 803 def nth(self, n: int) -> Option[Item]: 804 """Returns the `n`th element of the iterator 805 806 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 807 value. 808 809 Note all elements before `n` will be skipped / consumed. 810 811 Example 812 ------- 813 ```py 814 a = into_iter([1, 2, 3]) 815 assert a.iter().nth(1) == Some(2) 816 ``` 817 """ 818 for _ in range(n): 819 try: 820 self.__next__() 821 except StopIteration: 822 return _option.NOTHING 823 824 return self.next() 825 826 def sum(self: Sum) -> int: 827 """Sums an iterator of a possible type `T` that can be converted to an integer. 828 829 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 830 831 Example 832 ------- 833 ```py 834 numbers: Iterator[str] = Iter(["1", "2", "3"]) 835 total = numbers.sum() 836 assert total == 6 837 ``` 838 """ 839 return sum(int(_) for _ in self) 840 841 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 842 """Calls `func` on each item in the iterator. 843 844 Example 845 ------- 846 ```py 847 iterator = Iter([1, 2, 3]) 848 iterator.for_each(lambda item: print(item)) 849 # 1 850 # 2 851 # 3 852 ``` 853 854 Parameters 855 ---------- 856 func: `collections.Callable[[Item], typing.Any]` 857 The function to call on each item in the iterator. 858 """ 859 for item in self: 860 func(item) 861 862 async def async_for_each( 863 self, 864 func: collections.Callable[ 865 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 866 ], 867 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 868 """Calls the async function on each item in the iterator *concurrently*. 869 870 Concurrently meaning that the next item will not wait for other items 871 to finish to execute, each item gets called in a separate task. 872 873 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 874 which will need to be handled by the caller. 875 876 Example 877 ------- 878 ```py 879 async def create_user(username: str) -> None: 880 await aiohttp.request("POST", f'.../{username}') 881 882 async def main(): 883 users = sain.into_iter(["Danny", "Flower"]) 884 match await users.async_for_each(lambda username: create_user(username)): 885 case Ok(result): 886 # all good 887 case Err(why): 888 print(f"couldn't gather all futures, err={why}") 889 ``` 890 891 Parameters 892 ---------- 893 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 894 The async function to call on each item in the iterator. 895 """ 896 return await futures.join(*(func(item) for item in self)) 897 898 def __reversed__(self) -> Iter[Item]: 899 return self.reversed() 900 901 def __repr__(self) -> str: 902 return "<Iterator>" 903 904 def __copy__(self) -> Cloned[Item]: 905 return self.cloned() 906 907 def __deepcopy__( 908 self, memo: collections.MutableMapping[int, typing.Any], / 909 ) -> Copied[Item]: 910 return self.copied() 911 912 def __len__(self) -> int: 913 warnings.warn( 914 "Calling `len(iterator)` on this iterator will consume it, " 915 "use `Iterator.count()` if you explicitly want to consume this iterator and get its length.\n" 916 "If you're trying to convert this iterator to a list, use `Iterator.collect()` instead.\n" 917 "WARNING: This method will eventually be removed in future versions.", 918 category=FutureWarning, 919 stacklevel=3, 920 ) 921 return self.count() 922 923 def __iter__(self) -> Iterator[Item]: 924 return self
An abstract interface for dealing with iterators.
This is exactly the same trait as core::iter::Iterator
trait from Rust.
This is the main interface that any type can implement by basically inheriting from it.
The method __next__
is the only method that needs to be implemented, You get all the other methods for free.
If you want to use a ready iterator for general purposes, Use Iter
or TrustedIter
. This interface is only for implementers
and type hints.
Example
@dataclass
class Counter(Iterator[int]):
start: int = 0
stop: int | None = None
# implement the required method.
def __next__(self) -> int:
result = self.start
self.start += 1
if self.stop is not None and result >= self.stop:
raise StopIteration
return result
counter = Counter(start=0, stop=10)
for i in counter.map(lambda x: x * 2): # multiply each number
...
Implementations
This class implements Iterator in Rust.
177 @staticmethod 178 @typing.final 179 def default() -> Empty[Item]: 180 """Return the default iterator for this type. It returns an empty iterator. 181 182 Example 183 ------- 184 ```py 185 it: Iterator[int] = Iter.default() 186 assert t.next().is_none() 187 ``` 188 """ 189 return Empty()
Return the default iterator for this type. It returns an empty iterator.
Example
it: Iterator[int] = Iter.default()
assert t.next().is_none()
199 @typing.final 200 def collect( 201 self, *, cast: collections.Callable[[Item], OtherItem] | None = None 202 ) -> collections.MutableSequence[Item] | collections.MutableSequence[OtherItem]: 203 """Collects all items in the iterator into a sequence. 204 205 Example 206 ------- 207 ```py 208 iterator = Iter(range(3)) 209 iterator.collect() 210 # [0, 1, 2, 3] 211 iterator.collect(cast=str) # Map each element and collect it. 212 # ['0', '1', '2', '3'] 213 ``` 214 215 Parameters 216 ---------- 217 cast: `T | None` 218 An optional type to cast the items into. 219 If not provided the items will be returned as it's original type. 220 """ 221 if cast is not None: 222 return [cast(i) for i in self] 223 224 return [_ for _ in self]
Collects all items in the iterator into a sequence.
Example
iterator = Iter(range(3))
iterator.collect()
# [0, 1, 2, 3]
iterator.collect(cast=str) # Map each element and collect it.
# ['0', '1', '2', '3']
Parameters
- cast (
T | None
): An optional type to cast the items into. If not provided the items will be returned as it's original type.
226 @typing.final 227 def collect_into(self, collection: Collector[Item]) -> None: 228 """Consume this iterator, extending all items in the iterator into a mutable `collection`. 229 230 if `collection` is a `MutableSequence[Item]`, this iterator will call `extend` on it, 231 and if it was a `set[Item]`, this will call `update` on it. 232 233 Example 234 ------- 235 ```py 236 iterator = Iter([1, 1, 2, 3, 4, 2, 6]) 237 uniques = set() 238 iterator.collect_into(uniques) 239 # assert uniques == {1, 2, 3, 4, 6} 240 ``` 241 242 Parameters 243 ---------- 244 collection: `MutableSequence[T]` | `set[T]` 245 The collection to extend the items in this iterator with. 246 """ 247 if isinstance(collection, collections.MutableSequence): 248 collection.extend(_ for _ in self) 249 else: 250 collection.update(_ for _ in self)
Consume this iterator, extending all items in the iterator into a mutable collection
.
if collection
is a MutableSequence[Item]
, this iterator will call extend
on it,
and if it was a set[Item]
, this will call update
on it.
Example
iterator = Iter([1, 1, 2, 3, 4, 2, 6])
uniques = set()
iterator.collect_into(uniques)
# assert uniques == {1, 2, 3, 4, 6}
Parameters
- collection (
MutableSequence[T]
|set[T]
): The collection to extend the items in this iterator with.
252 @typing.final 253 def to_vec(self) -> Vec[Item]: 254 """Consume this iterator, returning all of its elements in a `Vec[T]`. 255 256 Example 257 ------- 258 ```py 259 it = sain.iter.once(0) 260 vc = it.to_vec() 261 262 assert to_vec == [0] 263 ``` 264 """ 265 from sain.collections.vec import Vec 266 267 return Vec(_ for _ in self)
Consume this iterator, returning all of its elements in a Vec[T]
.
Example
it = sain.iter.once(0)
vc = it.to_vec()
assert to_vec == [0]
269 @typing.final 270 def sink(self) -> None: 271 """Consume all elements from this iterator, returning nothing. 272 273 Example 274 ------- 275 ```py 276 it = Iter((1, 2, 3)) 277 it.sink() 278 assert it.next().is_none() 279 ``` 280 """ 281 for _ in self: 282 pass
Consume all elements from this iterator, returning nothing.
Example
it = Iter((1, 2, 3))
it.sink()
assert it.next().is_none()
284 @typing.final 285 def raw_parts(self) -> collections.Generator[Item, None, None]: 286 """Decompose this iterator into a `Generator[Item]` that yields all of the remaining items. 287 288 ```py 289 it = Iter("cba") 290 sort = sorted(it.raw_parts()) 291 292 assert it.count() == 0 293 assert sort == ["a", "b", "c"] 294 ``` 295 """ 296 for item in self: 297 yield item
Decompose this iterator into a Generator[Item]
that yields all of the remaining items.
it = Iter("cba")
sort = sorted(it.raw_parts())
assert it.count() == 0
assert sort == ["a", "b", "c"]
299 def next(self) -> Option[Item]: 300 """Advance the iterator, Returning the next item, `Some(None)` if all items yielded. 301 302 Example 303 ------- 304 ```py 305 iterator = Iter(["1", "2"]) 306 assert iterator.next() == Some("1") 307 assert iterator.next() == Some("2") 308 assert iterator.next().is_none() 309 ``` 310 """ 311 try: 312 return _option.Some(self.__next__()) 313 except StopIteration: 314 # SAFETY: No more items in the iterator. 315 return _option.NOTHING
Advance the iterator, Returning the next item, Some(None)
if all items yielded.
Example
iterator = Iter(["1", "2"])
assert iterator.next() == Some("1")
assert iterator.next() == Some("2")
assert iterator.next().is_none()
317 def cloned(self) -> Cloned[Item]: 318 """Creates an iterator which shallow copies its elements by reference. 319 320 .. note:: 321 This method calls [`copy.copy()`](https://docs.python.org/3/library/copy.html) 322 on each item that is being yielded. 323 324 Example 325 ------- 326 ```py 327 @dataclass 328 class User: 329 users_ids: list[int] = [] 330 331 # An iterator which elements points to the same user. 332 user = User() 333 it = Iter((user, user)) 334 335 for u in it.cloned(): 336 u.user_ids.append(1) 337 338 # We iterated over the same user pointer twice and appended "1" 339 # since `copy` returns a shallow copy of nested structures. 340 assert len(user.user_ids) == 2 341 ``` 342 """ 343 return Cloned(self)
Creates an iterator which shallow copies its elements by reference.
This method calls copy.copy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.cloned():
u.user_ids.append(1)
# We iterated over the same user pointer twice and appended "1"
# since `copy` returns a shallow copy of nested structures.
assert len(user.user_ids) == 2
345 def copied(self) -> Copied[Item]: 346 """Creates an iterator which copies all of its elements by value. 347 348 If you only need a copy of the item reference, Use `.cloned()` instead. 349 350 .. note:: 351 This method simply calls [`copy.deepcopy()`](https://docs.python.org/3/library/copy.html) 352 on each item that is being yielded. 353 354 Example 355 ------- 356 ```py 357 @dataclass 358 class User: 359 users_ids: list[int] = [] 360 361 # An iterator which elements points to the same user. 362 user = User() 363 it = Iter((user, user)) 364 365 for u in it.copied(): 366 # A new list is created for each item. 367 u.user_ids.append(1) 368 369 # The actual list is untouched since we consumed a deep copy of it. 370 assert len(user.user_ids) == 0 371 ``` 372 """ 373 return Copied(self)
Creates an iterator which copies all of its elements by value.
If you only need a copy of the item reference, Use .cloned()
instead.
This method simply calls copy.deepcopy()
on each item that is being yielded.
Example
@dataclass
class User:
users_ids: list[int] = []
# An iterator which elements points to the same user.
user = User()
it = Iter((user, user))
for u in it.copied():
# A new list is created for each item.
u.user_ids.append(1)
# The actual list is untouched since we consumed a deep copy of it.
assert len(user.user_ids) == 0
375 def map(self, fn: collections.Callable[[Item], OtherItem]) -> Map[Item, OtherItem]: 376 """Maps each item in the iterator to another type. 377 378 Example 379 ------- 380 ```py 381 iterator = Iter(["1", "2", "3"]).map(int) 382 383 for item in iterator: 384 assert isinstance(item, int) 385 ``` 386 387 Parameters 388 ---------- 389 predicate: `Callable[[Item], OtherItem]` 390 The function to map each item in the iterator to the other type. 391 """ 392 return Map(self, fn)
Maps each item in the iterator to another type.
Example
iterator = Iter(["1", "2", "3"]).map(int)
for item in iterator:
assert isinstance(item, int)
Parameters
- predicate (
Callable[[Item], OtherItem]
): The function to map each item in the iterator to the other type.
394 def filter(self, predicate: collections.Callable[[Item], bool]) -> Filter[Item]: 395 """Filters the iterator to only yield items that match the predicate. 396 397 Example 398 ------- 399 ```py 400 places = Iter(['London', 'Paris', 'Los Angeles']) 401 for place in places.filter(lambda place: place.startswith('L')): 402 print(place) 403 404 # London 405 # Los Angeles 406 ``` 407 """ 408 return Filter(self, predicate)
Filters the iterator to only yield items that match the predicate.
Example
places = Iter(['London', 'Paris', 'Los Angeles'])
for place in places.filter(lambda place: place.startswith('L')):
print(place)
# London
# Los Angeles
410 def take(self, count: int) -> Take[Item]: 411 """Take the first number of items until the number of items 412 are yielded or the end of the iterator is exhausted. 413 414 Example 415 ------- 416 ```py 417 iterator = Iter(['c', 'x', 'y']) 418 419 for x in iterator.take(2): 420 assert x in ('c', 'x') 421 422 # <Iter(['c', 'x'])> 423 ``` 424 """ 425 return Take(self, count)
Take the first number of items until the number of items are yielded or the end of the iterator is exhausted.
Example
iterator = Iter(['c', 'x', 'y'])
for x in iterator.take(2):
assert x in ('c', 'x')
# <Iter(['c', 'x'])>
427 def skip(self, count: int) -> Skip[Item]: 428 """Skips the first number of items in the iterator. 429 430 Example 431 ------- 432 ```py 433 iterator = Iter((1, 2, 3, 4)) 434 for i in iterator.skip(2): 435 print(i) 436 437 # 3 438 # 4 439 ``` 440 """ 441 return Skip(self, count)
Skips the first number of items in the iterator.
Example
iterator = Iter((1, 2, 3, 4))
for i in iterator.skip(2):
print(i)
# 3
# 4
443 def enumerate(self, *, start: int = 0) -> Enumerate[Item]: 444 """Create a new iterator that yields a tuple of the index and item. 445 446 Example 447 ------- 448 ```py 449 iterator = Iter([1, 2, 3]) 450 for index, item in iterator.enumerate(): 451 print(index, item) 452 453 # 0 1 454 # 1 2 455 # 2 3 456 ``` 457 """ 458 return Enumerate(self, start)
Create a new iterator that yields a tuple of the index and item.
Example
iterator = Iter([1, 2, 3])
for index, item in iterator.enumerate():
print(index, item)
# 0 1
# 1 2
# 2 3
460 def take_while(self, f: collections.Callable[[Item], bool]) -> TakeWhile[Item]: 461 """yields items from the iterator while predicate returns `True`. 462 463 The rest of the items are discarded as soon as the predicate returns `False` 464 465 Example 466 ------- 467 ```py 468 iterator = Iter(['a', 'ab', 'xd', 'ba']) 469 for x in iterator.take_while(lambda x: 'a' in x): 470 print(x) 471 472 # a 473 # ab 474 ``` 475 476 Parameters 477 ---------- 478 predicate: `collections.Callable[[Item], bool]` 479 The function to predicate each item in the iterator. 480 """ 481 return TakeWhile(self, f)
yields items from the iterator while predicate returns True
.
The rest of the items are discarded as soon as the predicate returns False
Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.take_while(lambda x: 'a' in x):
print(x)
# a
# ab
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to predicate each item in the iterator.
483 def drop_while(self, f: collections.Callable[[Item], bool]) -> DropWhile[Item]: 484 """Yields items from the iterator while predicate returns `False`. 485 486 Example 487 ------- 488 ```py 489 iterator = Iter(['a', 'ab', 'xd', 'ba']) 490 for x in iterator.drop_while(lambda x: 'a' in x): 491 print(x) 492 493 # xd 494 # ba 495 ``` 496 497 Parameters 498 ---------- 499 predicate: `collections.Callable[[Item], bool]` 500 The function to predicate each item in the iterator. 501 """ 502 return DropWhile(self, f)
Yields items from the iterator while predicate returns False
.
Example
iterator = Iter(['a', 'ab', 'xd', 'ba'])
for x in iterator.drop_while(lambda x: 'a' in x):
print(x)
# xd
# ba
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to predicate each item in the iterator.
504 def chunks(self, chunk_size: int, /) -> Chunks[Item]: 505 """Returns an iterator over `chunk_size` elements of the iterator at a time, 506 starting at the beginning of the iterator. 507 508 Example 509 ------- 510 ```py 511 iter = Iter(['a', 'b', 'c', 'd', 'e']) 512 chunks = iter.chunks() 513 assert chunks.next().unwrap() == ['a', 'b'] 514 assert chunks.next().unwrap() == ['c', 'd'] 515 assert chunks.next().unwrap() == ['e'] 516 assert chunks.next().is_none() 517 ``` 518 """ 519 return Chunks(self, chunk_size)
Returns an iterator over chunk_size
elements of the iterator at a time,
starting at the beginning of the iterator.
Example
iter = Iter(['a', 'b', 'c', 'd', 'e'])
chunks = iter.chunks()
assert chunks.next().unwrap() == ['a', 'b']
assert chunks.next().unwrap() == ['c', 'd']
assert chunks.next().unwrap() == ['e']
assert chunks.next().is_none()
521 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 522 """Return `True` if all items in the iterator match the predicate. 523 524 Example 525 ------- 526 ```py 527 iterator = Iter([1, 2, 3]) 528 if iterator.all(lambda item: isinstance(item, int)): 529 print("yes") 530 ``` 531 532 Parameters 533 ---------- 534 predicate: `collections.Callable[[Item], bool]` 535 The function to test each item in the iterator. 536 """ 537 return all(predicate(item) for item in self)
Return True
if all items in the iterator match the predicate.
Example
iterator = Iter([1, 2, 3])
if iterator.all(lambda item: isinstance(item, int)):
print("yes")
Parameters
- predicate (
collections.Callable[[Item], bool]
): The function to test each item in the iterator.
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 xs = Iter([1, 2, 3]) 567 ys = [4, 5, 6] 568 569 iter = xs.zip(ys) 570 571 assert iter.next().unwrap() == (1, 4) 572 assert iter.next().unwrap() == (2, 5) 573 assert iter.next().unwrap() == (3, 6) 574 ``` 575 576 Parameters 577 ---------- 578 other: `Iterable[OtherItem]` 579 The iterable to zip with. 580 """ 581 return Iter(zip(self.raw_parts(), other))
Zips the iterator with another iterable.
Example
xs = Iter([1, 2, 3])
ys = [4, 5, 6]
iter = xs.zip(ys)
assert iter.next().unwrap() == (1, 4)
assert iter.next().unwrap() == (2, 5)
assert iter.next().unwrap() == (3, 6)
Parameters
- other (
Iterable[OtherItem]
): The iterable to zip with.
583 def sort( 584 self, 585 *, 586 key: collections.Callable[[Item], SupportsRichComparison] | None = None, 587 reverse: bool = False, 588 ) -> collections.MutableSequence[Item]: 589 """Sorts the iterator elements and return it in a mutable sequence. 590 591 Example 592 ------- 593 ```py 594 iterator = Iter([3, 1, 6, 7]) 595 for item in iterator.sort(key=lambda item: item < 3): 596 print(item) 597 # 1 598 # 3 599 # 6 600 # 7 601 ``` 602 603 Parameters 604 ---------- 605 key: `collections.Callable[[Item], Any]` 606 The function to sort by. 607 reverse: `bool` 608 Whether to reverse the sort. 609 """ 610 return sorted([_ for _ in self], key=key, reverse=reverse) # pyright: ignore - key can be None here
Sorts the iterator elements and return it in a mutable sequence.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.sort(key=lambda item: item < 3):
print(item)
# 1
# 3
# 6
# 7
Parameters
- key (
collections.Callable[[Item], Any]
): The function to sort by. - reverse (
bool
): Whether to reverse the sort.
612 def reversed(self) -> Iter[Item]: 613 """Returns a new iterator that yields the items in the iterator in reverse order. 614 615 This consumes this iterator into a sequence and return a new iterator containing all of the elements 616 in reversed order. 617 618 Example 619 ------- 620 ```py 621 iterator = Iter([3, 1, 6, 7]) 622 for item in iterator.reversed(): 623 print(item) 624 # 7 625 # 6 626 # 1 627 # 3 628 ``` 629 """ 630 # NOTE: In order to reverse the iterator we need to 631 # first collect it into some collection. 632 return Iter(reversed([_ for _ in self]))
Returns a new iterator that yields the items in the iterator in reverse order.
This consumes this iterator into a sequence and return a new iterator containing all of the elements in reversed order.
Example
iterator = Iter([3, 1, 6, 7])
for item in iterator.reversed():
print(item)
# 7
# 6
# 1
# 3
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 """Consume this iterator, returning the count of elements it has. 686 687 Example 688 ------- 689 ```py 690 it = Iter(range(3)) 691 assert it.count() == 3 692 ``` 693 """ 694 count = 0 695 for _ in self: 696 count += 1 697 698 return count
Consume this iterator, returning the count of elements it has.
Example
it = Iter(range(3))
assert it.count() == 3
700 def find(self, predicate: collections.Callable[[Item], bool]) -> Option[Item]: 701 """Searches for an element of an iterator that satisfies a predicate. 702 703 If you want the position of the element, use `Iterator.position` instead. 704 705 `find()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 706 and if any of them return true, then find() returns `Some(element)`. If they all return false, it returns None. 707 708 Example 709 ------- 710 ```py 711 it = Iter(range(10)) 712 item = it.find(lambda num: num > 5) 713 print(item) # 6 714 ``` 715 """ 716 for item in self: 717 if predicate(item): 718 return _option.Some(item) 719 720 # no more items 721 return _option.NOTHING
Searches for an element of an iterator that satisfies a predicate.
If you want the position of the element, use Iterator.position
instead.
find()
takes a lambda that returns true or false. It applies this closure to each element of the iterator,
and if any of them return true, then find() returns Some(element)
. If they all return false, it returns None.
Example
it = Iter(range(10))
item = it.find(lambda num: num > 5)
print(item) # 6
723 def position(self, predicate: collections.Callable[[Item], bool]) -> Option[int]: 724 """Searches for the position of an element in the iterator that satisfies a predicate. 725 726 If you want the object itself, use `Iterator.find` instead. 727 728 `position()` takes a lambda that returns true or false. It applies this closure to each element of the iterator, 729 and if any of them return true, then position() returns `Some(position_of_element)`. If they all return false, it returns None. 730 731 Example 732 ------- 733 ```py 734 it = Iter(range(10)) 735 position = it.find(lambda num: num > 5) 736 assert position.unwrap() == 6 737 ``` 738 """ 739 for position, value in self.enumerate(): 740 if predicate(value): 741 return _option.Some(position) 742 743 # no more items 744 return _option.NOTHING
Searches for the position of an element in the iterator that satisfies a predicate.
If you want the object itself, use Iterator.find
instead.
position()
takes a lambda that returns true or false. It applies this closure to each element of the iterator,
and if any of them return true, then position() returns Some(position_of_element)
. If they all return false, it returns None.
Example
it = Iter(range(10))
position = it.find(lambda num: num > 5)
assert position.unwrap() == 6
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 advance_by(self, n: int) -> _result.Result[None, int]: 779 """Advances the iterator by `n` elements. 780 781 Returns `Result[None, int]`, where `Ok(None)` means the iterator 782 advanced successfully, and `Err(int)` if `None` encountered, where `int` 783 represents the remaining number of steps that could not be advanced because the iterator ran out. 784 785 Example 786 ------- 787 ```py 788 it = into_iter([1, 2, 3, 4]) 789 assert it.advance_by(2).is_ok() 790 assert it.next() == Some(3) 791 assert it.advance_by(0).is_ok() 792 assert it.advance_by(100) == Err(99) 793 ``` 794 """ 795 for i in range(n): 796 try: 797 self.__next__() 798 except StopIteration: 799 return _result.Err(n - i) 800 801 return _result.Ok(None)
Advances the iterator by n
elements.
Returns Result[None, int]
, where Ok(None)
means the iterator
advanced successfully, and Err(int)
if None
encountered, where int
represents the remaining number of steps that could not be advanced because the iterator ran out.
Example
it = into_iter([1, 2, 3, 4])
assert it.advance_by(2).is_ok()
assert it.next() == Some(3)
assert it.advance_by(0).is_ok()
assert it.advance_by(100) == Err(99)
803 def nth(self, n: int) -> Option[Item]: 804 """Returns the `n`th element of the iterator 805 806 Just like normal indexing, the count `n` starts from zero, so `nth(0)` returns the first 807 value. 808 809 Note all elements before `n` will be skipped / consumed. 810 811 Example 812 ------- 813 ```py 814 a = into_iter([1, 2, 3]) 815 assert a.iter().nth(1) == Some(2) 816 ``` 817 """ 818 for _ in range(n): 819 try: 820 self.__next__() 821 except StopIteration: 822 return _option.NOTHING 823 824 return self.next()
Returns the n
th element of the iterator
Just like normal indexing, the count n
starts from zero, so nth(0)
returns the first
value.
Note all elements before n
will be skipped / consumed.
Example
a = into_iter([1, 2, 3])
assert a.iter().nth(1) == Some(2)
826 def sum(self: Sum) -> int: 827 """Sums an iterator of a possible type `T` that can be converted to an integer. 828 829 where `T` is a typeof (`int`, `float`, `str`, `ReadableBuffer`, `SupportsTrunc`, `SupportsIndex`). 830 831 Example 832 ------- 833 ```py 834 numbers: Iterator[str] = Iter(["1", "2", "3"]) 835 total = numbers.sum() 836 assert total == 6 837 ``` 838 """ 839 return sum(int(_) for _ in self)
Sums an iterator of a possible type T
that can be converted to an integer.
where T
is a typeof (int
, float
, str
, ReadableBuffer
, SupportsTrunc
, SupportsIndex
).
Example
numbers: Iterator[str] = Iter(["1", "2", "3"])
total = numbers.sum()
assert total == 6
841 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 842 """Calls `func` on each item in the iterator. 843 844 Example 845 ------- 846 ```py 847 iterator = Iter([1, 2, 3]) 848 iterator.for_each(lambda item: print(item)) 849 # 1 850 # 2 851 # 3 852 ``` 853 854 Parameters 855 ---------- 856 func: `collections.Callable[[Item], typing.Any]` 857 The function to call on each item in the iterator. 858 """ 859 for item in self: 860 func(item)
Calls func
on each item in the iterator.
Example
iterator = Iter([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
- func (
collections.Callable[[Item], typing.Any]
): The function to call on each item in the iterator.
862 async def async_for_each( 863 self, 864 func: collections.Callable[ 865 [Item], collections.Coroutine[typing.Any, typing.Any, OtherItem] 866 ], 867 ) -> _result.Result[collections.Sequence[OtherItem], futures.JoinError]: 868 """Calls the async function on each item in the iterator *concurrently*. 869 870 Concurrently meaning that the next item will not wait for other items 871 to finish to execute, each item gets called in a separate task. 872 873 After all the tasks finish, a `Result[list[T], JoinError]` will be returned, 874 which will need to be handled by the caller. 875 876 Example 877 ------- 878 ```py 879 async def create_user(username: str) -> None: 880 await aiohttp.request("POST", f'.../{username}') 881 882 async def main(): 883 users = sain.into_iter(["Danny", "Flower"]) 884 match await users.async_for_each(lambda username: create_user(username)): 885 case Ok(result): 886 # all good 887 case Err(why): 888 print(f"couldn't gather all futures, err={why}") 889 ``` 890 891 Parameters 892 ---------- 893 func: `collections.Callable[[Item], Coroutine[None, Any, Any]]` 894 The async function to call on each item in the iterator. 895 """ 896 return await futures.join(*(func(item) for item in self))
Calls the async function on each item in the iterator concurrently.
Concurrently meaning that the next item will not wait for other items to finish to execute, each item gets called in a separate task.
After all the tasks finish, a Result[list[T], JoinError]
will be returned,
which will need to be handled by the caller.
Example
async def create_user(username: str) -> None:
await aiohttp.request("POST", f'.../{username}')
async def main():
users = sain.into_iter(["Danny", "Flower"])
match await users.async_for_each(lambda username: create_user(username)):
case Ok(result):
# all good
case Err(why):
print(f"couldn't gather all futures, err={why}")
Parameters
- func (
collections.Callable[[Item], Coroutine[None, Any, Any]]
): The async function to call on each item in the iterator.
744@rustc_diagnostic_item("todo") 745def todo(message: LiteralString | None = None) -> typing_extensions.Never: 746 """A place holder that indicates unfinished code. 747 748 Example 749 ------- 750 ```py 751 from sain import todo 752 753 def from_json(payload: dict[str, int]) -> int: 754 # Calling this function will raise `RuntimeError`. 755 todo() 756 ``` 757 758 Parameters 759 ---------- 760 message : `str | None` 761 Multiple optional arguments to pass if the error was raised. 762 """ 763 raise RuntimeError( 764 f"not yet implemented: {message}" if message else "not yet implemented" 765 )
A place holder that indicates unfinished code.
Example
from sain import todo
def from_json(payload: dict[str, int]) -> int:
# Calling this function will raise `RuntimeError`.
todo()
Parameters
- message (
str | None
): Multiple optional arguments to pass if the error was raised.
Implementations
This function implements todo in Rust.
671@rustc_diagnostic_item("deprecated") 672def deprecated( 673 *, 674 since: typing.Literal["CURRENT_VERSION"] | LiteralString | None = None, 675 removed_in: LiteralString | None = None, 676 use_instead: LiteralString | None = None, 677 hint: LiteralString | None = None, 678): 679 """A decorator that marks a function as deprecated. 680 681 Has no runtime side-effects. 682 683 Example 684 ------- 685 ```py 686 from sain import deprecated 687 688 @deprecated( 689 since = "1.0.0", 690 removed_in ="3.0.0", 691 use_instead = "UserImpl()", 692 hint = "Hint for ux." 693 ) 694 class User: 695 # calling the decorator is not necessary. 696 @deprecated 697 def username(self) -> str: 698 ... 699 700 user = User() # This will cause a warning at runtime. 701 702 ``` 703 704 Parameters 705 ---------- 706 since : `str` 707 The version that the function was deprecated. the `CURRENT_VERSION` is used internally only. 708 removed_in : `str | None` 709 If provided, It will log when will the object will be removed in. 710 use_instead : `str | None` 711 If provided, This should be the alternative object name that should be used instead. 712 hint: `str` 713 An optional hint for the user. 714 """ 715 716 def _create_message() -> LiteralString: 717 msg = "" 718 719 if since is not None: 720 if since == "CURRENT_VERSION": 721 from ._misc import __version__ 722 723 msg += " since " + __version__ 724 else: 725 msg += " since " + since 726 727 if removed_in: 728 msg += f" Scheduled for removal in `{removed_in}`." 729 730 if use_instead is not None: 731 msg += f" Use `{use_instead}` instead." 732 733 if hint: 734 msg += f" Hint: {hint}" 735 return msg 736 737 msg = _create_message() 738 if sys.version_info >= (3, 13, 0): 739 return warnings.deprecated(msg, stacklevel=3, category=None) 740 741 return typing_extensions.deprecated(msg, stacklevel=3, category=None)
A decorator that marks a function as deprecated.
Has no runtime side-effects.
Example
from sain import deprecated
@deprecated(
since = "1.0.0",
removed_in ="3.0.0",
use_instead = "UserImpl()",
hint = "Hint for ux."
)
class User:
# calling the decorator is not necessary.
@deprecated
def username(self) -> str:
...
user = User() # This will cause a warning at runtime.
Parameters
- since (
str
): The version that the function was deprecated. theCURRENT_VERSION
is used internally only. - removed_in (
str | None
): If provided, It will log when will the object will be removed in. - use_instead (
str | None
): If provided, This should be the alternative object name that should be used instead. - hint (
str
): An optional hint for the user.
Implementations
This function implements deprecated in Rust.
768@rustc_diagnostic_item("unimplemented") 769def unimplemented(message: LiteralString | None = None, /) -> typing_extensions.Never: 770 """Marks an object as unimplemented. 771 772 Raises `RuntimeError` if called. 773 774 Example 775 ------- 776 ```py 777 from sain import unimplemented 778 779 class User: 780 unimplemented() 781 782 match parse_http_method(inp): 783 case "GET": ... 784 case _: unimplemented("not yet") 785 ``` 786 787 Parameters 788 ---------- 789 message : `str | None` 790 An optional message to be displayed when the function is called. Otherwise default message will be used. 791 """ 792 raise RuntimeError( 793 "not implemented" if not message else f"not implemented: {message}" 794 ) from None
Marks an object as unimplemented.
Raises RuntimeError
if called.
Example
from sain import unimplemented
class User:
unimplemented()
match parse_http_method(inp):
case "GET": ...
case _: unimplemented("not yet")
Parameters
- message (
str | None
): An optional message to be displayed when the function is called. Otherwise default message will be used.
Implementations
This function implements unimplemented in Rust.
797@rustc_diagnostic_item("doc") 798def doc( 799 path: Read, 800) -> collections.Callable[ 801 [collections.Callable[P, U]], 802 collections.Callable[P, U], 803]: 804 """Set `path` to be the object's documentation. 805 806 Example 807 ------- 808 ```py 809 from sain import doc 810 from pathlib import Path 811 812 @doc(Path("../README.md")) 813 class builtins: 814 @doc("bool.html") 815 def bool_docs() -> None: 816 ... 817 ``` 818 819 Parameters 820 ---------- 821 path: `type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]` 822 The path to read the content from. 823 """ 824 825 def decorator(f: collections.Callable[P, U]) -> collections.Callable[P, U]: 826 with open(path, "r") as file: 827 f.__doc__ = file.read() 828 829 return lambda *args, **kwargs: f(*args, **kwargs) 830 831 return decorator
Set path
to be the object's documentation.
Example
from sain import doc
from pathlib import Path
@doc(Path("../README.md"))
class builtins:
@doc("bool.html")
def bool_docs() -> None:
...
Parameters
- path (
type[int] | type[str] | type[bytes] | type[PathLike[str]] | type[PathLike[bytes]]
): The path to read the content from.
Implementations
This function implements doc in Rust.
549@rustc_diagnostic_item("include_str") 550def include_str(file: LiteralString) -> LiteralString: 551 """Includes a file as literal `str`. 552 553 This function is not magic, It is literally defined as 554 555 ```py 556 with open(file, "r") as f: 557 return f.read() 558 ``` 559 560 The file name can may be either a relative to the current file or a complete path. 561 562 Example 563 ------- 564 ```py 565 from sain.macros import include_str 566 567 def entry() -> None: 568 ... 569 570 entry.__doc__ = include_str("README.md") 571 572 ``` 573 """ 574 with open(file, "r") as buf: 575 return buf.read() # pyright: ignore - simulates a `&'static str` slice.
Includes a file as literal str
.
This function is not magic, It is literally defined as
with open(file, "r") as f:
return f.read()
The file name can may be either a relative to the current file or a complete path.
Example
from sain.macros import include_str
def entry() -> None:
...
entry.__doc__ = include_str("README.md")
Implementations
This function implements include_str in Rust.
519@rustc_diagnostic_item("include_bytes") 520def include_bytes(file: LiteralString) -> bytes: 521 """Includes a file as `bytes`. 522 523 This function is not magic, It is literally defined as 524 525 ```py 526 with open(file, "rb") as f: 527 return f.read() 528 ``` 529 530 The file name can may be either a relative to the current file or a complete path. 531 532 Example 533 ------- 534 File "spanish.in": 535 ```text 536 adiós 537 ``` 538 File "main.py": 539 ```py 540 from sain.macros import include_bytes 541 buffer = include_bytes("spanish.in") 542 assert buffer.decode() == "adiós" 543 ``` 544 """ 545 with open(file, "rb") as buf: 546 return buf.read()
Includes a file as bytes
.
This function is not magic, It is literally defined as
with open(file, "rb") as f:
return f.read()
The file name can may be either a relative to the current file or a complete path.
Example
File "spanish.in":
adiós
File "main.py":
from sain.macros import include_bytes
buffer = include_bytes("spanish.in")
assert buffer.decode() == "adiós"
Implementations
This function implements include_bytes in Rust.
479@rustc_diagnostic_item("assert_eq") 480def assert_eq(left: T, right: T) -> None: 481 """Asserts that two expressions are equal to each other. 482 483 This exactly as `assert left == right`, but includes a useful message in case of failure. 484 485 Example 486 ------- 487 ```py 488 from sain.macros import assert_eq 489 a = 3 490 b = 1 + 2 491 assert_eq(a, b) 492 ``` 493 """ 494 assert left == right, ( 495 f'assertion `left == right` failed\nleft: "{left!r}"\nright: "{right!r}"' 496 )
Asserts that two expressions are equal to each other.
This exactly as assert left == right
, but includes a useful message in case of failure.
Example
from sain.macros import assert_eq
a = 3
b = 1 + 2
assert_eq(a, b)
Implementations
This function implements assert_eq in Rust.
499@rustc_diagnostic_item("assert_ne") 500def assert_ne(left: T, right: T) -> None: 501 """Asserts that two expressions are not equal to each other. 502 503 This exactly as `assert left == right`, but includes a useful message in case of failure. 504 505 Example 506 ------- 507 ```py 508 from sain.macros import assert_ne 509 a = 3 510 b = 2 + 2 511 assert_ne(a, b) 512 ``` 513 """ 514 assert left != right, ( 515 f'assertion `left != right` failed\nleft: "{left!r}"\nright: "{right!r}"' 516 )
Asserts that two expressions are not equal to each other.
This exactly as assert left == right
, but includes a useful message in case of failure.
Example
from sain.macros import assert_ne
a = 3
b = 2 + 2
assert_ne(a, b)
Implementations
This function implements assert_ne in Rust.
118@rustc_diagnostic_item("Ok") 119@typing.final 120@dataclasses.dataclass(slots=True, frozen=True, repr=False) 121class Ok(typing.Generic[T]): 122 """Contains the success value of `Result[T, ...]`.""" 123 124 _inner: T 125 126 ############################### 127 # * Querying operations. * # 128 ############################### 129 130 def is_ok(self) -> typing.Literal[True]: 131 """Returns `True` if the contained value is `Ok` and `False` if it an `Err`. 132 133 Example 134 ------- 135 ```py 136 value: Result[str, None] = Ok("value") 137 assert value.is_ok() == True 138 ``` 139 """ 140 return True 141 142 def is_ok_and(self, f: F[T, bool]) -> bool: 143 """Returns `True` if the contained value is `Ok` and `f()` returns True. 144 145 Example 146 ------- 147 ```py 148 value: Result[str, None] = Ok("value") 149 assert value.is_ok_and(lambda inner: inner == "value") 150 # True 151 ``` 152 """ 153 return f(self._inner) 154 155 # These are never truthy in an `Ok` instance. 156 def is_err(self) -> typing.Literal[False]: 157 """Returns `True` if the contained value is `Err`. 158 159 Example 160 ------- 161 ```py 162 value: Result[str, None] = Ok("value") 163 164 assert value.is_err() == False 165 ``` 166 """ 167 return False 168 169 def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]: 170 """Returns `True` if the contained value is `Ok` and `f()` returns True. 171 172 Example 173 ------- 174 ```py 175 value: Result[str, None] = Ok("value") 176 177 assert value.is_err_and(lambda inner: inner == "value") 178 # False 179 ``` 180 """ 181 return False 182 183 ################### 184 # * Extractors * # 185 ################### 186 187 def expect(self, message: str, /) -> T: 188 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 189 if it was `Err` with `message` passed to it. 190 191 Example 192 ------- 193 ```py 194 ok: Result[str, None] = Ok("owo") 195 ok.expect("err") # owo 196 197 err: Result[str, None] = Err(None) 198 err.expect("err") # RuntimeError("err") 199 ``` 200 """ 201 return self._inner 202 203 def expect_err(self) -> Never: 204 """Return the `Err` value if `self` is an `Err`, panicking otherwise. 205 206 Example 207 ------- 208 ```py 209 ok: Result[str, None] = Ok("owo") 210 ok.expect_err() # RuntimeError("Called expect_err on `Ok`) 211 212 err: Result[str, None] = Err(None) 213 err.expect_err() # None 214 ``` 215 """ 216 raise RuntimeError("Called `expect_err` on an `Ok` value.") 217 218 def unwrap(self) -> T: 219 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 220 221 Example 222 ------- 223 ```py 224 ok: Result[str, None] = Ok("owo") 225 ok.unwrap() # owo 226 227 err: Result[str, None] = Err(None) 228 err.unwrap() # RuntimeError 229 ``` 230 """ 231 return self._inner 232 233 def unwrap_or(self, default: T, /) -> T: 234 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 235 236 Example 237 ------- 238 ```py 239 ok: Result[str, None] = Ok("OwO") 240 ok.unwrap_or("uwu") # OwO 241 242 err: Result[str, None] = Err(None) 243 err.unwrap_or("uwu") # uwu 244 ``` 245 """ 246 return self._inner 247 248 def unwrap_or_else(self, f: F[E, T]) -> T: 249 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 250 251 Example 252 ------- 253 ```py 254 ok: Result[int, str] = Ok(4) 255 ok.unwrap_or_else(lambda e: 0) # 4 256 257 err: Result[int, str] = Err("word") 258 err.unwrap_or_else(lambda e: len(e)) # 4 259 ``` 260 """ 261 return self._inner 262 263 def unwrap_err(self) -> Never: 264 """Return the contained `Err` value, Raising if it was `Ok`. 265 266 Example 267 ------- 268 ```py 269 ok: Result[str, None] = Ok("buh") 270 ok.unwrap_err() # RuntimeError 271 272 err: Result[str, None] = Err(None) 273 err.unwrap_err() == None 274 # True 275 ``` 276 """ 277 raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}") 278 279 ############################ 280 # * Conversion adapters. * # 281 ############################ 282 283 def ok(self) -> Option[T]: 284 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 285 286 Example 287 ------- 288 ```py 289 value: Result[str, None] = Ok("buh") 290 value.ok().is_some() # True 291 292 value: Result[str, int] = Err(0) 293 value.ok().is_none() # True 294 ``` 295 """ 296 return _option.Some(self._inner) 297 298 def err(self) -> Option[T]: 299 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 300 301 Example 302 ------- 303 ```py 304 value: Result[str, None] = Ok("buh") 305 value.err().is_none() # True 306 307 value: Result[str, int] = Err(0) 308 value.err().is_some() # True 309 ``` 310 """ 311 return _option.NOTHING 312 313 def inspect(self, f: F[T, typing.Any]) -> Self: 314 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 315 316 Example 317 ------- 318 ```py 319 def sink(value: str) -> None: 320 # do something with value 321 print("Called " + value) 322 323 x: Result[str, None] = Ok("ok") 324 x.inspect(sink) # "Called ok" 325 326 x: Result[str, str] = Err("err") 327 x.inspect(sink) # None 328 ``` 329 """ 330 f(self._inner) 331 return self 332 333 def inspect_err(self, f: F[E, typing.Any]) -> Self: 334 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 335 336 Example 337 ------- 338 ```py 339 def sink(value: str) -> None: 340 # do something with value 341 print("Called " + value) 342 343 x: Result[str, None] = Ok("ok") 344 x.inspect_err(sink) # None 345 346 x: Result[str, str] = Err("err") 347 x.inspect_err(sink) # Called err 348 ``` 349 """ 350 return self 351 352 def map(self, f: F[T, U], /) -> Ok[U]: 353 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 354 leaving `Err` untouched. 355 356 Example 357 ------- 358 ```py 359 ok: Result[str, int] = Ok("1") 360 ok.map(lambda c: int(c) + 1) # Ok(2) 361 362 err: Result[str, int] = Err(0) 363 err.map(str.upper) # Err(0) 364 ``` 365 """ 366 return Ok(f(self._inner)) 367 368 def map_or(self, f: F[T, U], default: U, /) -> U: 369 """Returns the provided `default` if the contained value is `Err`, 370 371 Otherwise extracts the `Ok` value and maps it to `f()` 372 373 Example 374 ------- 375 ```py 376 x: Result[str, str] = Ok("foo") 377 assert x.map_or(lambda c: len(c), 42) == 3 378 379 x: Result[str, str] = Err("bar") 380 assert x.map_or(lambda c: len(c), 42) == 42 381 ``` 382 """ 383 return f(self._inner) 384 385 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 386 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 387 or function `f` to a contained Ok value. 388 389 Example 390 ------- 391 ```py 392 x: Result[str, str] = Ok("four") 393 assert x.map_or_else( 394 lambda ok: 2 * len(ok), 395 default=lambda err: len(err) 396 ) == 8 397 398 x: Result[str, str] = Err("bar") 399 assert x.map_or_else( 400 lambda c: 2 * len(c), 401 lambda err: len(err) 402 ) == 3 403 ``` 404 """ 405 return f(self._inner) 406 407 def map_err(self, f: F[E, U], /) -> Self: 408 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 409 410 Example 411 ------- 412 ```py 413 x: Result[str, int] = Ok("blue") 414 x.map_err(lambda err: err + 1) # Ok("blue") 415 416 x: Result[str, int] = Err(5) 417 x.map_err(float) # Err(5.0) 418 ``` 419 """ 420 return self 421 422 ############################## 423 # * Iterator constructors. * # 424 ############################## 425 426 def iter(self) -> _iter.ExactSizeIterator[T]: 427 """An iterator over the possible contained value. 428 429 If `self` was `Ok`, then the iterator will yield the Ok `T`. otherwise yields nothing. 430 431 Example 432 ------- 433 ```py 434 c: Result[str, int] = Ok("blue") 435 c.iter().next() == Some("blue") 436 437 c: Result[str, int] = Err(0) 438 c.iter().next() == Some(None) 439 ``` 440 """ 441 return _iter.Once(self._inner) 442 443 def __iter__(self) -> collections.Iterator[T]: 444 yield self._inner 445 446 ################# 447 # * Overloads * # 448 ################# 449 450 def __repr__(self) -> str: 451 return f"Ok({self._inner!r})" 452 453 def __or__(self, other: T) -> T: 454 return self._inner 455 456 def __invert__(self) -> T: 457 return self._inner
Contains the success value of Result[T, ...]
.
Implementations
142 def is_ok_and(self, f: F[T, bool]) -> bool: 143 """Returns `True` if the contained value is `Ok` and `f()` returns True. 144 145 Example 146 ------- 147 ```py 148 value: Result[str, None] = Ok("value") 149 assert value.is_ok_and(lambda inner: inner == "value") 150 # True 151 ``` 152 """ 153 return f(self._inner)
Returns True
if the contained value is Ok
and f()
returns True.
Example
value: Result[str, None] = Ok("value")
assert value.is_ok_and(lambda inner: inner == "value")
# True
156 def is_err(self) -> typing.Literal[False]: 157 """Returns `True` if the contained value is `Err`. 158 159 Example 160 ------- 161 ```py 162 value: Result[str, None] = Ok("value") 163 164 assert value.is_err() == False 165 ``` 166 """ 167 return False
Returns True
if the contained value is Err
.
Example
value: Result[str, None] = Ok("value")
assert value.is_err() == False
169 def is_err_and(self, f: F[T, bool]) -> typing.Literal[False]: 170 """Returns `True` if the contained value is `Ok` and `f()` returns True. 171 172 Example 173 ------- 174 ```py 175 value: Result[str, None] = Ok("value") 176 177 assert value.is_err_and(lambda inner: inner == "value") 178 # False 179 ``` 180 """ 181 return False
Returns True
if the contained value is Ok
and f()
returns True.
Example
value: Result[str, None] = Ok("value")
assert value.is_err_and(lambda inner: inner == "value")
# False
187 def expect(self, message: str, /) -> T: 188 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 189 if it was `Err` with `message` passed to it. 190 191 Example 192 ------- 193 ```py 194 ok: Result[str, None] = Ok("owo") 195 ok.expect("err") # owo 196 197 err: Result[str, None] = Err(None) 198 err.expect("err") # RuntimeError("err") 199 ``` 200 """ 201 return self._inner
203 def expect_err(self) -> Never: 204 """Return the `Err` value if `self` is an `Err`, panicking otherwise. 205 206 Example 207 ------- 208 ```py 209 ok: Result[str, None] = Ok("owo") 210 ok.expect_err() # RuntimeError("Called expect_err on `Ok`) 211 212 err: Result[str, None] = Err(None) 213 err.expect_err() # None 214 ``` 215 """ 216 raise RuntimeError("Called `expect_err` on an `Ok` value.")
218 def unwrap(self) -> T: 219 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 220 221 Example 222 ------- 223 ```py 224 ok: Result[str, None] = Ok("owo") 225 ok.unwrap() # owo 226 227 err: Result[str, None] = Err(None) 228 err.unwrap() # RuntimeError 229 ``` 230 """ 231 return self._inner
233 def unwrap_or(self, default: T, /) -> T: 234 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 235 236 Example 237 ------- 238 ```py 239 ok: Result[str, None] = Ok("OwO") 240 ok.unwrap_or("uwu") # OwO 241 242 err: Result[str, None] = Err(None) 243 err.unwrap_or("uwu") # uwu 244 ``` 245 """ 246 return self._inner
248 def unwrap_or_else(self, f: F[E, T]) -> T: 249 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 250 251 Example 252 ------- 253 ```py 254 ok: Result[int, str] = Ok(4) 255 ok.unwrap_or_else(lambda e: 0) # 4 256 257 err: Result[int, str] = Err("word") 258 err.unwrap_or_else(lambda e: len(e)) # 4 259 ``` 260 """ 261 return self._inner
263 def unwrap_err(self) -> Never: 264 """Return the contained `Err` value, Raising if it was `Ok`. 265 266 Example 267 ------- 268 ```py 269 ok: Result[str, None] = Ok("buh") 270 ok.unwrap_err() # RuntimeError 271 272 err: Result[str, None] = Err(None) 273 err.unwrap_err() == None 274 # True 275 ``` 276 """ 277 raise RuntimeError(f"Called `unwrap_err` on an `Ok` variant: {self._inner!r}")
283 def ok(self) -> Option[T]: 284 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 285 286 Example 287 ------- 288 ```py 289 value: Result[str, None] = Ok("buh") 290 value.ok().is_some() # True 291 292 value: Result[str, int] = Err(0) 293 value.ok().is_none() # True 294 ``` 295 """ 296 return _option.Some(self._inner)
Transform Result[T, E]
to Option[T]
, mapping Ok(v)
to Some(T)
and Err(e)
to None
.
Example
value: Result[str, None] = Ok("buh")
value.ok().is_some() # True
value: Result[str, int] = Err(0)
value.ok().is_none() # True
298 def err(self) -> Option[T]: 299 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 300 301 Example 302 ------- 303 ```py 304 value: Result[str, None] = Ok("buh") 305 value.err().is_none() # True 306 307 value: Result[str, int] = Err(0) 308 value.err().is_some() # True 309 ``` 310 """ 311 return _option.NOTHING
Transform Result[T, E]
to Option[E]
, mapping Ok(v)
to None
and Err(e)
to Some(e)
.
Example
value: Result[str, None] = Ok("buh")
value.err().is_none() # True
value: Result[str, int] = Err(0)
value.err().is_some() # True
313 def inspect(self, f: F[T, typing.Any]) -> Self: 314 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 315 316 Example 317 ------- 318 ```py 319 def sink(value: str) -> None: 320 # do something with value 321 print("Called " + value) 322 323 x: Result[str, None] = Ok("ok") 324 x.inspect(sink) # "Called ok" 325 326 x: Result[str, str] = Err("err") 327 x.inspect(sink) # None 328 ``` 329 """ 330 f(self._inner) 331 return self
333 def inspect_err(self, f: F[E, typing.Any]) -> Self: 334 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 335 336 Example 337 ------- 338 ```py 339 def sink(value: str) -> None: 340 # do something with value 341 print("Called " + value) 342 343 x: Result[str, None] = Ok("ok") 344 x.inspect_err(sink) # None 345 346 x: Result[str, str] = Err("err") 347 x.inspect_err(sink) # Called err 348 ``` 349 """ 350 return self
352 def map(self, f: F[T, U], /) -> Ok[U]: 353 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 354 leaving `Err` untouched. 355 356 Example 357 ------- 358 ```py 359 ok: Result[str, int] = Ok("1") 360 ok.map(lambda c: int(c) + 1) # Ok(2) 361 362 err: Result[str, int] = Err(0) 363 err.map(str.upper) # Err(0) 364 ``` 365 """ 366 return Ok(f(self._inner))
368 def map_or(self, f: F[T, U], default: U, /) -> U: 369 """Returns the provided `default` if the contained value is `Err`, 370 371 Otherwise extracts the `Ok` value and maps it to `f()` 372 373 Example 374 ------- 375 ```py 376 x: Result[str, str] = Ok("foo") 377 assert x.map_or(lambda c: len(c), 42) == 3 378 379 x: Result[str, str] = Err("bar") 380 assert x.map_or(lambda c: len(c), 42) == 42 381 ``` 382 """ 383 return f(self._inner)
385 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 386 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 387 or function `f` to a contained Ok value. 388 389 Example 390 ------- 391 ```py 392 x: Result[str, str] = Ok("four") 393 assert x.map_or_else( 394 lambda ok: 2 * len(ok), 395 default=lambda err: len(err) 396 ) == 8 397 398 x: Result[str, str] = Err("bar") 399 assert x.map_or_else( 400 lambda c: 2 * len(c), 401 lambda err: len(err) 402 ) == 3 403 ``` 404 """ 405 return f(self._inner)
Maps a Resultdefault
to a contained Err value,
or function f
to a contained Ok value.
Example
x: Result[str, str] = Ok("four")
assert x.map_or_else(
lambda ok: 2 * len(ok),
default=lambda err: len(err)
) == 8
x: Result[str, str] = Err("bar")
assert x.map_or_else(
lambda c: 2 * len(c),
lambda err: len(err)
) == 3
407 def map_err(self, f: F[E, U], /) -> Self: 408 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 409 410 Example 411 ------- 412 ```py 413 x: Result[str, int] = Ok("blue") 414 x.map_err(lambda err: err + 1) # Ok("blue") 415 416 x: Result[str, int] = Err(5) 417 x.map_err(float) # Err(5.0) 418 ``` 419 """ 420 return self
Maps a Result[T, E]
to Result[T, U]
by applying function f
, leaving the Ok
value untouched.
Example
x: Result[str, int] = Ok("blue")
x.map_err(lambda err: err + 1) # Ok("blue")
x: Result[str, int] = Err(5)
x.map_err(float) # Err(5.0)
426 def iter(self) -> _iter.ExactSizeIterator[T]: 427 """An iterator over the possible contained value. 428 429 If `self` was `Ok`, then the iterator will yield the Ok `T`. otherwise yields nothing. 430 431 Example 432 ------- 433 ```py 434 c: Result[str, int] = Ok("blue") 435 c.iter().next() == Some("blue") 436 437 c: Result[str, int] = Err(0) 438 c.iter().next() == Some(None) 439 ``` 440 """ 441 return _iter.Once(self._inner)
An iterator over the possible contained value.
If self
was Ok
, then the iterator will yield the Ok T
. otherwise yields nothing.
Example
c: Result[str, int] = Ok("blue")
c.iter().next() == Some("blue")
c: Result[str, int] = Err(0)
c.iter().next() == Some(None)
460@rustc_diagnostic_item("Err") 461@typing.final 462@dataclasses.dataclass(slots=True, frozen=True, repr=False) 463class Err(typing.Generic[E]): 464 """Contains the error value of `Result[..., E]`.""" 465 466 _inner: E 467 468 ################################ 469 # * Boolean operations. * # 470 ################################ 471 472 def is_ok(self) -> typing.Literal[False]: 473 """Returns `True` if the contained value is `Ok` and `False` if it an `Err`. 474 475 Example 476 ------- 477 ```py 478 value: Result[str, None] = Err(None) 479 480 assert value.is_ok() == False 481 ``` 482 """ 483 return False 484 485 def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]: 486 """Returns `True` if the contained value is `Ok` and `f()` returns True. 487 488 Example 489 ------- 490 ```py 491 value: Result[str, None] = Err(None) 492 493 assert value.is_ok_and(lambda inner: inner == "value") 494 # False 495 ``` 496 """ 497 return False 498 499 # These are never truthy in an `Ok` instance. 500 def is_err(self) -> typing.Literal[True]: 501 """Returns `True` if the contained value is `Err`. 502 503 Example 504 ------- 505 ```py 506 value: Result[str, None] = Err(None) 507 508 assert value.is_err() == True 509 ``` 510 """ 511 return True 512 513 def is_err_and(self, f: F[E, bool]) -> bool: 514 """Returns `True` if the contained value is `Ok` and `f()` returns True.. 515 516 Example 517 ------- 518 ```py 519 value: Result[str, None] = Err(None) 520 521 assert value.is_err_and(lambda err: err is None) 522 # True 523 ``` 524 """ 525 return f(self._inner) 526 527 ################### 528 # * Extractors. * # 529 ################### 530 531 def expect(self, msg: str) -> Never: 532 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 533 if it was `Err` with `message` passed to it. 534 535 Example 536 ------- 537 ```py 538 ok: Result[str, None] = Ok("owo") 539 ok.expect("err") # owo 540 541 err: Result[str, None] = Err(None) 542 err.expect("err") # RuntimeError("err") 543 ``` 544 """ 545 raise RuntimeError(msg) from None 546 547 def expect_err(self) -> E: 548 """Return the `Err` if it was `Err`, panicking otherwise. 549 550 551 Example 552 ------- 553 ```py 554 x: Result[str, None] = Ok("owo") 555 x.expect_err() # RuntimeError("Called expect_err on `Ok`) 556 557 x: Result[str, None] = Err(None) 558 x.expect_err() # None 559 ``` 560 """ 561 return self._inner 562 563 def unwrap(self) -> Never: 564 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 565 566 Example 567 ------- 568 ```py 569 ok: Result[str, None] = Ok("owo") 570 ok.unwrap() # owo 571 572 err: Result[str, None] = Err(None) 573 err.unwrap() # RuntimeError 574 ``` 575 """ 576 raise RuntimeError( 577 f"Called `unwrap()` on an `Err` variant: {self._inner!r}" 578 ) from None 579 580 def unwrap_or(self, default: T, /) -> T: 581 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 582 583 Example 584 ------- 585 ```py 586 ok: Result[str, None] = Ok("OwO") 587 ok.unwrap_or("uwu") # OwO 588 589 err: Result[str, None] = Err(None) 590 err.unwrap_or("uwu") # uwu 591 ``` 592 """ 593 return default 594 595 def unwrap_or_else(self, f: F[E, T]) -> T: 596 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 597 598 Example 599 ------- 600 ```py 601 ok: Result[int, str] = Ok(4) 602 ok.unwrap_or_else(lambda e: 0) # 4 603 604 err: Result[int, str] = Err("word") 605 err.unwrap_or_else(lambda e: len(e)) # 4 606 ``` 607 """ 608 return f(self._inner) 609 610 def unwrap_err(self) -> E: 611 """Return the contained `Err` value, Raising if it was `Ok`. 612 613 Example 614 ------- 615 ```py 616 ok: Result[str, None] = Ok("buh") 617 ok.unwrap_err() # RuntimeError 618 619 err: Result[str, None] = Err(None) 620 err.unwrap_err() == None 621 # True 622 ``` 623 """ 624 return self._inner 625 626 ############################ 627 # * Conversion adapters. * # 628 ############################ 629 630 def inspect(self, f: F[T, typing.Any]) -> Self: 631 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 632 633 Example 634 ------- 635 ```py 636 def sink(value: str) -> None: 637 # do something with value 638 print("Called " + value) 639 640 x: Result[str, None] = Ok("ok") 641 x.inspect(sink) # "Called ok" 642 643 x: Result[str, str] = Err("err") 644 x.inspect(sink) # None 645 ``` 646 """ 647 return self 648 649 def inspect_err(self, f: F[E, typing.Any]) -> Self: 650 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 651 652 Example 653 ------- 654 ```py 655 def sink(value: str) -> None: 656 # do something with value 657 print("Called " + value) 658 659 x: Result[str, None] = Ok("ok") 660 x.inspect_err(sink) # None 661 662 x: Result[str, str] = Err("err") 663 x.inspect_err(sink) # Called err 664 ``` 665 """ 666 f(self._inner) 667 return self 668 669 def ok(self) -> Option[None]: 670 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 671 672 Example 673 ------- 674 ```py 675 value: Result[str, None] = Ok("buh") 676 value.ok().is_some() # True 677 678 value: Result[str, int] = Err(0) 679 value.ok().is_none() # True 680 ``` 681 """ 682 return _option.NOTHING 683 684 def err(self) -> Option[E]: 685 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 686 687 Example 688 ------- 689 ```py 690 value: Result[str, None] = Ok("buh") 691 value.err().is_none() # True 692 693 value: Result[str, int] = Err(0) 694 value.err().is_some() # True 695 ``` 696 """ 697 return _option.Some(self._inner) 698 699 def map(self, f: F[E, U]) -> Self: 700 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 701 leaving `Err` untouched. 702 703 Example 704 ------- 705 ```py 706 ok: Result[str, int] = Ok("1") 707 ok.map(lambda c: int(c) + 1) # Ok(2) 708 709 err: Result[str, int] = Err(0) 710 err.map(str.upper) # Err(0) 711 ``` 712 """ 713 return self 714 715 def map_or(self, f: F[E, U], default: U, /) -> U: 716 """Returns the provided `default` if the contained value is `Err`, 717 718 Otherwise extracts the `Ok` value and maps it to `f()` 719 720 Example 721 ------- 722 ```py 723 x: Result[str, str] = Ok("foo") 724 assert x.map_or(lambda c: len(c), 42) == 3 725 726 x: Result[str, str] = Err("bar") 727 assert x.map_or(lambda c: len(c), 42) == 42 728 ``` 729 """ 730 return default 731 732 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 733 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 734 or function `f` to a contained Ok value. 735 736 Example 737 ------- 738 ```py 739 x: Result[str, str] = Ok("four") 740 assert x.map_or_else( 741 lambda ok: 2 * len(ok), 742 default=lambda err: len(err) 743 ) == 8 744 745 x: Result[str, str] = Err("bar") 746 assert x.map_or_else( 747 lambda c: 2 * len(c), 748 lambda err: len(err) 749 ) == 3 750 ``` 751 """ 752 return default(self._inner) 753 754 def map_err(self, f: F[E, U]) -> Err[U]: 755 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 756 757 Example 758 ------- 759 ```py 760 x: Result[str, int] = Ok("blue") 761 x.map_err(lambda err: err + 1) # Ok("blue") 762 763 x: Result[str, int] = Err(5) 764 x.map_err(float) # Err(5.0) 765 ``` 766 """ 767 return Err(f(self._inner)) 768 769 ############################## 770 # * Iterator constructors. * # 771 ############################## 772 773 def iter(self) -> _iter.ExactSizeIterator[E]: 774 """An iterator over the possible contained value. 775 776 If `self` was `Ok`, then the iterator will yield `T`, otherwise yields nothing. 777 778 Example 779 ------- 780 ```py 781 c: Result[str, int] = Ok("blue") 782 c.iter().next() == Some("blue") 783 784 c: Result[str, int] = Err(0) 785 c.iter().next() == Some(None) 786 ``` 787 """ 788 return _iter.Empty() 789 790 def __iter__(self) -> collections.Iterator[E]: 791 yield from () 792 793 ################# 794 # * Overloads * # 795 ################# 796 797 def __repr__(self) -> str: 798 return f"Err({self._inner!r})" 799 800 def __or__(self, other: T) -> T: 801 return other 802 803 def __invert__(self) -> Never: 804 self.unwrap()
Contains the error value of Result[..., E]
.
Implementations
This class implements .Result.html#variant.Err">Err in Rust.
485 def is_ok_and(self, f: F[E, bool]) -> typing.Literal[False]: 486 """Returns `True` if the contained value is `Ok` and `f()` returns True. 487 488 Example 489 ------- 490 ```py 491 value: Result[str, None] = Err(None) 492 493 assert value.is_ok_and(lambda inner: inner == "value") 494 # False 495 ``` 496 """ 497 return False
Returns True
if the contained value is Ok
and f()
returns True.
Example
value: Result[str, None] = Err(None)
assert value.is_ok_and(lambda inner: inner == "value")
# False
500 def is_err(self) -> typing.Literal[True]: 501 """Returns `True` if the contained value is `Err`. 502 503 Example 504 ------- 505 ```py 506 value: Result[str, None] = Err(None) 507 508 assert value.is_err() == True 509 ``` 510 """ 511 return True
Returns True
if the contained value is Err
.
Example
value: Result[str, None] = Err(None)
assert value.is_err() == True
513 def is_err_and(self, f: F[E, bool]) -> bool: 514 """Returns `True` if the contained value is `Ok` and `f()` returns True.. 515 516 Example 517 ------- 518 ```py 519 value: Result[str, None] = Err(None) 520 521 assert value.is_err_and(lambda err: err is None) 522 # True 523 ``` 524 """ 525 return f(self._inner)
Returns True
if the contained value is Ok
and f()
returns True..
Example
value: Result[str, None] = Err(None)
assert value.is_err_and(lambda err: err is None)
# True
531 def expect(self, msg: str) -> Never: 532 """Return the underlying value if it was `Ok`, Raising `RuntimeError` 533 if it was `Err` with `message` passed to it. 534 535 Example 536 ------- 537 ```py 538 ok: Result[str, None] = Ok("owo") 539 ok.expect("err") # owo 540 541 err: Result[str, None] = Err(None) 542 err.expect("err") # RuntimeError("err") 543 ``` 544 """ 545 raise RuntimeError(msg) from None
547 def expect_err(self) -> E: 548 """Return the `Err` if it was `Err`, panicking otherwise. 549 550 551 Example 552 ------- 553 ```py 554 x: Result[str, None] = Ok("owo") 555 x.expect_err() # RuntimeError("Called expect_err on `Ok`) 556 557 x: Result[str, None] = Err(None) 558 x.expect_err() # None 559 ``` 560 """ 561 return self._inner
563 def unwrap(self) -> Never: 564 """Return the underlying value if it was `Ok`, Raising `RuntimeError` if it was `Err`. 565 566 Example 567 ------- 568 ```py 569 ok: Result[str, None] = Ok("owo") 570 ok.unwrap() # owo 571 572 err: Result[str, None] = Err(None) 573 err.unwrap() # RuntimeError 574 ``` 575 """ 576 raise RuntimeError( 577 f"Called `unwrap()` on an `Err` variant: {self._inner!r}" 578 ) from None
580 def unwrap_or(self, default: T, /) -> T: 581 """Return the underlying value if it was `Ok`, returning `default` if it was `Err`. 582 583 Example 584 ------- 585 ```py 586 ok: Result[str, None] = Ok("OwO") 587 ok.unwrap_or("uwu") # OwO 588 589 err: Result[str, None] = Err(None) 590 err.unwrap_or("uwu") # uwu 591 ``` 592 """ 593 return default
595 def unwrap_or_else(self, f: F[E, T]) -> T: 596 """Return the contained `Ok` value or computes it from `f()` if it was `Err`. 597 598 Example 599 ------- 600 ```py 601 ok: Result[int, str] = Ok(4) 602 ok.unwrap_or_else(lambda e: 0) # 4 603 604 err: Result[int, str] = Err("word") 605 err.unwrap_or_else(lambda e: len(e)) # 4 606 ``` 607 """ 608 return f(self._inner)
610 def unwrap_err(self) -> E: 611 """Return the contained `Err` value, Raising if it was `Ok`. 612 613 Example 614 ------- 615 ```py 616 ok: Result[str, None] = Ok("buh") 617 ok.unwrap_err() # RuntimeError 618 619 err: Result[str, None] = Err(None) 620 err.unwrap_err() == None 621 # True 622 ``` 623 """ 624 return self._inner
630 def inspect(self, f: F[T, typing.Any]) -> Self: 631 """Call a function to the contained value if it was `Ok` and do nothing if it was `Err` 632 633 Example 634 ------- 635 ```py 636 def sink(value: str) -> None: 637 # do something with value 638 print("Called " + value) 639 640 x: Result[str, None] = Ok("ok") 641 x.inspect(sink) # "Called ok" 642 643 x: Result[str, str] = Err("err") 644 x.inspect(sink) # None 645 ``` 646 """ 647 return self
649 def inspect_err(self, f: F[E, typing.Any]) -> Self: 650 """Call a function to the contained value if it was `Err` and do nothing if it was `Ok` 651 652 Example 653 ------- 654 ```py 655 def sink(value: str) -> None: 656 # do something with value 657 print("Called " + value) 658 659 x: Result[str, None] = Ok("ok") 660 x.inspect_err(sink) # None 661 662 x: Result[str, str] = Err("err") 663 x.inspect_err(sink) # Called err 664 ``` 665 """ 666 f(self._inner) 667 return self
669 def ok(self) -> Option[None]: 670 """Transform `Result[T, E]` to `Option[T]`, mapping `Ok(v)` to `Some(T)` and `Err(e)` to `None`. 671 672 Example 673 ------- 674 ```py 675 value: Result[str, None] = Ok("buh") 676 value.ok().is_some() # True 677 678 value: Result[str, int] = Err(0) 679 value.ok().is_none() # True 680 ``` 681 """ 682 return _option.NOTHING
Transform Result[T, E]
to Option[T]
, mapping Ok(v)
to Some(T)
and Err(e)
to None
.
Example
value: Result[str, None] = Ok("buh")
value.ok().is_some() # True
value: Result[str, int] = Err(0)
value.ok().is_none() # True
684 def err(self) -> Option[E]: 685 """Transform `Result[T, E]` to `Option[E]`, mapping `Ok(v)` to `None` and `Err(e)` to `Some(e)`. 686 687 Example 688 ------- 689 ```py 690 value: Result[str, None] = Ok("buh") 691 value.err().is_none() # True 692 693 value: Result[str, int] = Err(0) 694 value.err().is_some() # True 695 ``` 696 """ 697 return _option.Some(self._inner)
Transform Result[T, E]
to Option[E]
, mapping Ok(v)
to None
and Err(e)
to Some(e)
.
Example
value: Result[str, None] = Ok("buh")
value.err().is_none() # True
value: Result[str, int] = Err(0)
value.err().is_some() # True
699 def map(self, f: F[E, U]) -> Self: 700 """Map `Result<T, E>` to `Result<U, E>` by applying a function to the `Ok` value, 701 leaving `Err` untouched. 702 703 Example 704 ------- 705 ```py 706 ok: Result[str, int] = Ok("1") 707 ok.map(lambda c: int(c) + 1) # Ok(2) 708 709 err: Result[str, int] = Err(0) 710 err.map(str.upper) # Err(0) 711 ``` 712 """ 713 return self
715 def map_or(self, f: F[E, U], default: U, /) -> U: 716 """Returns the provided `default` if the contained value is `Err`, 717 718 Otherwise extracts the `Ok` value and maps it to `f()` 719 720 Example 721 ------- 722 ```py 723 x: Result[str, str] = Ok("foo") 724 assert x.map_or(lambda c: len(c), 42) == 3 725 726 x: Result[str, str] = Err("bar") 727 assert x.map_or(lambda c: len(c), 42) == 42 728 ``` 729 """ 730 return default
732 def map_or_else(self, f: F[T, U], default: F[E, U], /) -> U: 733 """Maps a Result<T, E> to U by applying fallback function `default` to a contained Err value, 734 or function `f` to a contained Ok value. 735 736 Example 737 ------- 738 ```py 739 x: Result[str, str] = Ok("four") 740 assert x.map_or_else( 741 lambda ok: 2 * len(ok), 742 default=lambda err: len(err) 743 ) == 8 744 745 x: Result[str, str] = Err("bar") 746 assert x.map_or_else( 747 lambda c: 2 * len(c), 748 lambda err: len(err) 749 ) == 3 750 ``` 751 """ 752 return default(self._inner)
Maps a Resultdefault
to a contained Err value,
or function f
to a contained Ok value.
Example
x: Result[str, str] = Ok("four")
assert x.map_or_else(
lambda ok: 2 * len(ok),
default=lambda err: len(err)
) == 8
x: Result[str, str] = Err("bar")
assert x.map_or_else(
lambda c: 2 * len(c),
lambda err: len(err)
) == 3
754 def map_err(self, f: F[E, U]) -> Err[U]: 755 """Maps a `Result[T, E]` to `Result[T, U]` by applying function `f`, leaving the `Ok` value untouched. 756 757 Example 758 ------- 759 ```py 760 x: Result[str, int] = Ok("blue") 761 x.map_err(lambda err: err + 1) # Ok("blue") 762 763 x: Result[str, int] = Err(5) 764 x.map_err(float) # Err(5.0) 765 ``` 766 """ 767 return Err(f(self._inner))
Maps a Result[T, E]
to Result[T, U]
by applying function f
, leaving the Ok
value untouched.
Example
x: Result[str, int] = Ok("blue")
x.map_err(lambda err: err + 1) # Ok("blue")
x: Result[str, int] = Err(5)
x.map_err(float) # Err(5.0)
773 def iter(self) -> _iter.ExactSizeIterator[E]: 774 """An iterator over the possible contained value. 775 776 If `self` was `Ok`, then the iterator will yield `T`, otherwise yields nothing. 777 778 Example 779 ------- 780 ```py 781 c: Result[str, int] = Ok("blue") 782 c.iter().next() == Some("blue") 783 784 c: Result[str, int] = Err(0) 785 c.iter().next() == Some(None) 786 ``` 787 """ 788 return _iter.Empty()
An iterator over the possible contained value.
If self
was Ok
, then the iterator will yield T
, otherwise yields nothing.
Example
c: Result[str, int] = Ok("blue")
c.iter().next() == Some("blue")
c: Result[str, int] = Err(0)
c.iter().next() == Some(None)
162@rustc_diagnostic_item("Vec") 163@typing.final 164class Vec(SliceMut[T], collections.MutableSequence[T]): 165 """A basic implementation of Rust's `Vec<T>` type, backed by Python `list<T>`. 166 167 See the module top level documentation for more information. 168 169 Example 170 ------- 171 ```py 172 names = Vec[str]() 173 174 names.push('foo') 175 names.push('bar') 176 177 print(names) # ['foo', 'bar'] 178 assert names.len() == 2 179 ``` 180 """ 181 182 __slots__ = ("_buf", "_capacity") 183 184 @typing.overload 185 def __init__(self) -> None: 186 """Create an empty `Vec<T>`. 187 188 Example 189 ------ 190 ```py 191 vec: Vec[float] = Vec() 192 ``` 193 """ 194 195 @typing.overload 196 def __init__(self, iterable: collections.Iterable[T]) -> None: 197 """Create a new `Vec<T>` from an iterable. 198 199 Example 200 ------- 201 ```py 202 vec = Vec([1.2, 3.4]) 203 ``` 204 """ 205 206 def __init__(self, iterable: collections.Iterable[T] | None = None) -> None: 207 if isinstance(iterable, list): 208 # Calling `list()` on another list will copy it, So instead we just point to it. 209 self._buf = iterable 210 elif isinstance(iterable, Vec): 211 self._buf = iterable._buf 212 # any other iterable that ain't a list needs to get copied into a new list. 213 else: 214 self._buf = list(iterable) if iterable else [] 215 216 self._capacity: int | None = None 217 218 @classmethod 219 def with_capacity(cls, capacity: int) -> Vec[T]: 220 """Create a new `Vec` with at least the specified capacity. 221 This vec will be able to hold `capacity` elements without pushing further. 222 223 The capacity may dynamically grow if `Vec.reserve` is called at runtime. 224 225 Check out `Vec.push_within_capacity` as well. 226 227 Example 228 ------- 229 ```py 230 vec = Vec.with_capacity(3) 231 assert vec.len() == 0 and vec.capacity() >= 3 232 233 vec.push(1) 234 vec.push(2) 235 vec.push(3) 236 print(vec.len()) # 3 237 238 # This won't push. 239 vec.push(4) 240 ``` 241 """ 242 v = cls() 243 v._capacity = capacity 244 return v 245 246 def as_slice(self) -> Slice[T]: 247 """Return an immutable view over this vector elements. 248 249 No copying occurs. 250 251 Example 252 ------- 253 ```py 254 def send_bytes(v: Slice[int]) -> None: 255 # access `vec` in a read-only mode. 256 socket.write_all(v) 257 258 buf = Vec([1, 2, 3]) 259 send_bytes(buf.as_slice()) 260 ``` 261 """ 262 # NOTE: Although, we could just return `self`, but, 263 # we want to return an exclusive view. 264 return Slice(self._buf) 265 266 def as_mut_slice(self) -> SliceMut[T]: 267 """Return a mutable view over this vector elements. 268 269 Example 270 ------- 271 ```py 272 def read_bytes(buf: SliceMut[int]) -> None: 273 # similar to how `Write::write_to_end` requires a `&mut [u8]` 274 socket.read_to_end(buf) 275 276 buf: Vec[int] = Vec() 277 read_bytes(buf.as_mut_slice()) # or just `buf` 278 assert buf.len() >= 1 279 ``` 280 """ 281 return self 282 283 def capacity(self) -> int: 284 """Return the capacity of this vector if set, 0 if not . 285 286 The number `0` here has two different Meanings: 287 - The first means the `Vec` doesn't have a specific capacity set. 288 - The second means the `Vec` literally initialized with `0` capacity. 289 290 Example 291 ------- 292 ```py 293 vec_with_cap = Vec.with_capacity(3) 294 assert vec_with_cap.capacity().unwrap() == 3 295 296 vec = Vec([1, 2, 3]) 297 assert vec.capacity() == 0 298 ``` 299 """ 300 return 0 if self._capacity is None else self._capacity 301 302 def leak(self) -> list[T]: 303 """Consumes and leaks the Vec, returning a mutable reference to the contents. 304 305 After calling this, this vec will no longer reference the underlying buffer, 306 therefore, it becomes unusable. 307 308 if `self` is uninitialized, an empty list is returned. 309 310 This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without 311 any copies. 312 313 Example 314 ------- 315 ```py 316 x = Vec([1, 2, 3]) 317 owned = x.leak() 318 # x is now unusable. 319 owned[0] += 1 320 assert owned == [2, 2, 3] 321 ``` 322 """ 323 # don't point to `_buf` anymore. 324 tmp = self._buf 325 del self._buf 326 return tmp # pyright: ignore[reportReturnType] - _buf is a list. 327 328 def split_off(self, at: int) -> Vec[T]: 329 """Split the vector off at the specified position. 330 331 Returns a new vector at the range of `[at : len]`, leaving `self` at `[0: at]`. 332 333 if this vec is empty, `self` is returned unchanged. 334 335 Example 336 ------- 337 ```py 338 origin = Vec((1, 2, 3, 4)) 339 split = vec.split_off(2) 340 341 print(origin, split) # [1, 2], [3, 4] 342 ``` 343 344 Raises 345 ------ 346 `IndexError` 347 This method will raise if `at` > `len(self)` 348 """ 349 len_ = self.len() 350 if at > len_: 351 raise IndexError( 352 f"Index `at` ({at}) should be <= than len of vector ({len_}) " 353 ) from None 354 355 # Either the list is empty or uninit. 356 if not self._buf: 357 return self 358 359 split = self._buf[at:len_] 360 del self._buf[at:len_] 361 return Vec(split) 362 363 def truncate(self, size: int) -> None: 364 """Shortens the vec, keeping the first `size` elements and dropping the rest. 365 366 Example 367 ------- 368 ```py 369 vec = Vec([1,2,3]) 370 vec.truncate(1) 371 assert vec == [1] 372 ``` 373 """ 374 del self._buf[size:] 375 376 def retain(self, f: collections.Callable[[T], bool]) -> None: 377 """Retains only the elements specified by the predicate. 378 379 This operation occurs in-place without copying the original list. 380 381 Example 382 ------- 383 ```py 384 vec = Vec([1, 2, 3]) 385 vec.retain(lambda elem: elem > 1) 386 387 assert vec == [2, 3] 388 ``` 389 390 The impl for this method is very simple 391 ```py 392 for idx, e in enumerate(vec): 393 if not f(e): 394 del vec[idx] 395 ``` 396 """ 397 idx = 0 398 while idx < len(self._buf): 399 if not f(self._buf[idx]): 400 del self._buf[idx] 401 else: 402 idx += 1 403 404 def swap_remove(self, item: T) -> T: 405 """Remove the first appearance of `item` from this vector and return it. 406 407 Raises 408 ------ 409 `ValueError` 410 if `item` is not in this vector. 411 412 Example 413 ------- 414 ```py 415 vec = Vec(('a', 'b', 'c')) 416 element = vec.swap_remove('a') 417 assert vec == ['b', 'c'] and element == 'a' 418 ``` 419 """ 420 return self._buf.pop(self.index(item)) 421 422 def push(self, item: T) -> None: 423 """Push an element at the end of the vector. 424 425 Example 426 ------- 427 ```py 428 vec = Vec() 429 vec.push(1) 430 431 assert vec == [1] 432 ``` 433 """ 434 if (cap := self._capacity) is not None and len(self._buf) == cap: 435 # max cap reached. 436 return 437 438 self._buf.append(item) 439 440 def push_within_capacity(self, x: T) -> Result[None, T]: 441 """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element. 442 443 Example 444 ------- 445 ```py 446 vec: Vec[int] = Vec.with_capacity(3) 447 for i in range(3): 448 match vec.push_within_capacity(i): 449 case Ok(_): 450 print("All good.") 451 case Err(elem): 452 print("Reached max cap :< can't push", elem) 453 ``` 454 455 Or you can also just call `Vec.push` and it will push if there's is sufficient capacity. 456 ```py 457 vec: Vec[int] = Vec.with_capacity(3) 458 459 vec.extend((1, 2, 3)) 460 vec.push(4) 461 462 assert vec.len() == 3 463 ``` 464 """ 465 if self.len() == self._capacity: 466 return _result.Err(x) 467 468 self._buf.append(x) 469 return _result.Ok(None) 470 471 def reserve(self, additional: int) -> None: 472 """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. 473 474 Example 475 ------- 476 ```py 477 vec = Vec.with_capacity(3) 478 479 for i in range(3): 480 vec.push(i) 481 482 match vec.push_within_capacity(4): 483 case Ok(_): 484 print("Pushed 4 successfully.") 485 case Err(elem): 486 vec.reserve(1) # Reserve capacity for 1 more element. 487 vec.push(4) # Now we can push 4. 488 ``` 489 """ 490 if self._capacity is not None: 491 self._capacity += additional 492 493 def shrink_to_fit(self) -> None: 494 """Shrink the capacity of this `Vec` to match its length. 495 496 If `self` is initialized with no capacity, This is a `NOP`. 497 498 Example 499 ------- 500 ```py 501 s = Vec([1, 2, 3, 4]) 502 503 s.reserve(100) 504 s.shrink_to_fit() 505 assert s.capacity() == 4 506 ``` 507 """ 508 if self._capacity is None: 509 return 510 511 # The capacity is never less than the length. 512 self._capacity = min(self.__len__(), self._capacity) 513 514 def shrink_to(self, min_capacity: int) -> None: 515 """Shrink the capacity of this `Vec` to a minimum specified capacity. 516 517 If `self` is initialized with no capacity or the current capacity is less than the lower limit, 518 This is a `NOP`. 519 520 Example 521 ------- 522 ```py 523 vec = Vec.with_capacity(10) 524 vec.extend([1, 2, 3]) 525 assert vec.capacity() >= 10 526 vec.shrink_to(4) 527 assert vec.capacity() >= 4 528 vec.shrink_to(0) 529 ``` 530 """ 531 if self._capacity is None or self._capacity <= min_capacity: 532 return 533 534 # Ensure the capacity is not reduced below the current length of the vector. 535 self._capacity = max(self.__len__(), min_capacity) 536 537 ########################## 538 # * Builtin Operations * 539 ########################## 540 541 def append(self, value: T) -> None: 542 """Append an element at the end of the vector. 543 544 An alias to `Vec.push`. 545 """ 546 self._buf.append(value) 547 548 def insert(self, index: int, value: T) -> None: 549 """Insert an element at the position `index`. 550 551 Example 552 -------- 553 ```py 554 vec = Vec((2, 3)) 555 vec.insert(0, 1) 556 assert vec == [1, 2, 3] 557 ``` 558 """ 559 self[index] = value 560 561 def pop(self, index: int = -1) -> _option.Option[T]: 562 """Removes the last element from the vector and returns it, or `None` if it is empty. 563 564 Example 565 ------- 566 ```py 567 vec = Vec((1, 2, 3)) 568 assert vec.pop() == Some(3) 569 assert vec == [1, 2] 570 ``` 571 """ 572 if not self._buf: 573 return _option.NOTHING 574 575 return _option.Some(self._buf.pop(index)) 576 577 def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]: 578 """Removes the last element from a vector and returns it if `f` returns `True`, 579 or `None` if it is empty. 580 581 Example 582 ------- 583 ```py 584 vec = Vec((1, 2, 3)) 585 assert vec.pop_if(lambda num: num * 2 == 6) == Some(3) 586 assert vec == [1, 2] 587 ``` 588 """ 589 if not self._buf: 590 return _option.NOTHING 591 592 if pred(self[-1]): 593 return self.pop() 594 595 return _option.NOTHING 596 597 def dedup(self) -> None: 598 """Removes consecutive repeated elements in the vector according to their `__eq__` 599 implementation. 600 601 Example 602 ------- 603 ```py 604 vec = Vec([1, 2, 2, 3, 2]) 605 vec.dedup() 606 assert vec == [1, 2, 3, 2] 607 """ 608 self.dedup_by(lambda a, b: a == b) 609 610 def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None: 611 """Removes all but the first of consecutive elements in the vector satisfying a given equality. 612 613 Example 614 ------- 615 ```py 616 vec = Vec(["foo", "bar", "Bar", "baz", "bar"]) 617 vec.dedup_by(lambda a, b: a.lower() == b.lower()) 618 assert vec == ["foo", "bar", "baz", "bar"] 619 ``` 620 621 Only remove consecutive duplicates. 622 ```py 623 vec = Vec([1, 2, 2, 3, 4, 1]) 624 vec.dedup_by(lambda a, b: a == b) 625 # The final 1 is not adjacent to the first 1, so it is not removed. 626 assert vec == [1, 2, 3, 4, 1] 627 ``` 628 """ 629 630 if not self._buf or (len_ := len(self._buf)) <= 1: 631 return 632 633 idx = 1 634 while idx < len_: 635 if same_bucket(self._buf[idx], self._buf[idx - 1]): 636 del self._buf[idx] 637 len_ -= 1 638 else: 639 idx += 1 640 641 def remove(self, item: T) -> None: 642 """Remove the first appearance of `item` from this vector. 643 644 Example 645 ------- 646 ```py 647 vec = Vec(('a', 'b', 'c')) 648 vec.remove('a') 649 assert vec == ['b', 'c'] 650 ``` 651 """ 652 self._buf.remove(item) 653 654 def extend(self, iterable: collections.Iterable[T]) -> None: 655 """Extend this vector from another iterable. 656 657 Example 658 ------- 659 ```py 660 vec = Vec((1, 2, 3)) 661 vec.extend((4, 5, 6)) 662 663 assert vec == [1, 2, 3, 4, 5, 6] 664 ``` 665 """ 666 self._buf.extend(iterable) 667 668 def copy(self) -> Vec[T]: 669 """Copy all elements of `self` into a new vector. 670 671 Example 672 ------- 673 ```py 674 original = Vec((1,2,3)) 675 copy = original.copy() 676 copy.push(4) 677 678 print(original) # [1, 2, 3] 679 ``` 680 """ 681 return Vec(self._buf[:]) 682 683 def clear(self) -> None: 684 """Clear all elements of this vector. 685 686 Example 687 ------- 688 ```py 689 vec = Vec((1,2,3)) 690 vec.clear() 691 assert vec.len() == 0 692 ``` 693 """ 694 self._buf.clear() 695 696 def sort( 697 self, 698 *, 699 key: collections.Callable[[T], SupportsRichComparison] | None = None, 700 reverse: bool = False, 701 ) -> None: 702 """This method sorts the list in place, using only < comparisons between items. 703 704 Example 705 ------- 706 ```py 707 vec = Vec((2, 1, 3)) 708 vec.sort() 709 assert vec == [1, 2, 3] 710 ``` 711 """ 712 # key can be `None` here just fine, idk why pyright is complaining. 713 self._buf.sort(key=key, reverse=reverse) # pyright: ignore 714 715 def count(self, item: T) -> int: 716 """Return the number of occurrences of `item` in the vec. 717 718 `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found. 719 720 Example 721 -------- 722 ```py 723 vec = Vec((1, 2, 3, 3)) 724 assert vec.count(3) == 2 725 ``` 726 """ 727 return self._buf.count(item) 728 729 # lists are unhashable. 730 __hash__: None = None
A basic implementation of Rust's Vec<T>
type, backed by Python list<T>
.
See the module top level documentation for more information.
Example
names = Vec[str]()
names.push('foo')
names.push('bar')
print(names) # ['foo', 'bar']
assert names.len() == 2
Implementations
This class implements Vec in Rust.
206 def __init__(self, iterable: collections.Iterable[T] | None = None) -> None: 207 if isinstance(iterable, list): 208 # Calling `list()` on another list will copy it, So instead we just point to it. 209 self._buf = iterable 210 elif isinstance(iterable, Vec): 211 self._buf = iterable._buf 212 # any other iterable that ain't a list needs to get copied into a new list. 213 else: 214 self._buf = list(iterable) if iterable else [] 215 216 self._capacity: int | None = None
Create a new Vec<T>
from an iterable.
Example
vec = Vec([1.2, 3.4])
218 @classmethod 219 def with_capacity(cls, capacity: int) -> Vec[T]: 220 """Create a new `Vec` with at least the specified capacity. 221 This vec will be able to hold `capacity` elements without pushing further. 222 223 The capacity may dynamically grow if `Vec.reserve` is called at runtime. 224 225 Check out `Vec.push_within_capacity` as well. 226 227 Example 228 ------- 229 ```py 230 vec = Vec.with_capacity(3) 231 assert vec.len() == 0 and vec.capacity() >= 3 232 233 vec.push(1) 234 vec.push(2) 235 vec.push(3) 236 print(vec.len()) # 3 237 238 # This won't push. 239 vec.push(4) 240 ``` 241 """ 242 v = cls() 243 v._capacity = capacity 244 return v
Create a new Vec
with at least the specified capacity.
This vec will be able to hold capacity
elements without pushing further.
The capacity may dynamically grow if Vec.reserve
is called at runtime.
Check out Vec.push_within_capacity
as well.
Example
vec = Vec.with_capacity(3)
assert vec.len() == 0 and vec.capacity() >= 3
vec.push(1)
vec.push(2)
vec.push(3)
print(vec.len()) # 3
# This won't push.
vec.push(4)
246 def as_slice(self) -> Slice[T]: 247 """Return an immutable view over this vector elements. 248 249 No copying occurs. 250 251 Example 252 ------- 253 ```py 254 def send_bytes(v: Slice[int]) -> None: 255 # access `vec` in a read-only mode. 256 socket.write_all(v) 257 258 buf = Vec([1, 2, 3]) 259 send_bytes(buf.as_slice()) 260 ``` 261 """ 262 # NOTE: Although, we could just return `self`, but, 263 # we want to return an exclusive view. 264 return Slice(self._buf)
Return an immutable view over this vector elements.
No copying occurs.
Example
def send_bytes(v: Slice[int]) -> None:
# access `vec` in a read-only mode.
socket.write_all(v)
buf = Vec([1, 2, 3])
send_bytes(buf.as_slice())
266 def as_mut_slice(self) -> SliceMut[T]: 267 """Return a mutable view over this vector elements. 268 269 Example 270 ------- 271 ```py 272 def read_bytes(buf: SliceMut[int]) -> None: 273 # similar to how `Write::write_to_end` requires a `&mut [u8]` 274 socket.read_to_end(buf) 275 276 buf: Vec[int] = Vec() 277 read_bytes(buf.as_mut_slice()) # or just `buf` 278 assert buf.len() >= 1 279 ``` 280 """ 281 return self
Return a mutable view over this vector elements.
Example
def read_bytes(buf: SliceMut[int]) -> None:
# similar to how `Write::write_to_end` requires a `&mut [u8]`
socket.read_to_end(buf)
buf: Vec[int] = Vec()
read_bytes(buf.as_mut_slice()) # or just `buf`
assert buf.len() >= 1
283 def capacity(self) -> int: 284 """Return the capacity of this vector if set, 0 if not . 285 286 The number `0` here has two different Meanings: 287 - The first means the `Vec` doesn't have a specific capacity set. 288 - The second means the `Vec` literally initialized with `0` capacity. 289 290 Example 291 ------- 292 ```py 293 vec_with_cap = Vec.with_capacity(3) 294 assert vec_with_cap.capacity().unwrap() == 3 295 296 vec = Vec([1, 2, 3]) 297 assert vec.capacity() == 0 298 ``` 299 """ 300 return 0 if self._capacity is None else self._capacity
Return the capacity of this vector if set, 0 if not .
The number 0
here has two different Meanings:
- The first means the
Vec
doesn't have a specific capacity set. - The second means the
Vec
literally initialized with0
capacity.
Example
vec_with_cap = Vec.with_capacity(3)
assert vec_with_cap.capacity().unwrap() == 3
vec = Vec([1, 2, 3])
assert vec.capacity() == 0
302 def leak(self) -> list[T]: 303 """Consumes and leaks the Vec, returning a mutable reference to the contents. 304 305 After calling this, this vec will no longer reference the underlying buffer, 306 therefore, it becomes unusable. 307 308 if `self` is uninitialized, an empty list is returned. 309 310 This function is only useful when you want to detach from a `Vec<T>` and get a `list[T]` without 311 any copies. 312 313 Example 314 ------- 315 ```py 316 x = Vec([1, 2, 3]) 317 owned = x.leak() 318 # x is now unusable. 319 owned[0] += 1 320 assert owned == [2, 2, 3] 321 ``` 322 """ 323 # don't point to `_buf` anymore. 324 tmp = self._buf 325 del self._buf 326 return tmp # pyright: ignore[reportReturnType] - _buf is a list.
Consumes and leaks the Vec, returning a mutable reference to the contents.
After calling this, this vec will no longer reference the underlying buffer, therefore, it becomes unusable.
if self
is uninitialized, an empty list is returned.
This function is only useful when you want to detach from a Vec<T>
and get a list[T]
without
any copies.
Example
x = Vec([1, 2, 3])
owned = x.leak()
# x is now unusable.
owned[0] += 1
assert owned == [2, 2, 3]
328 def split_off(self, at: int) -> Vec[T]: 329 """Split the vector off at the specified position. 330 331 Returns a new vector at the range of `[at : len]`, leaving `self` at `[0: at]`. 332 333 if this vec is empty, `self` is returned unchanged. 334 335 Example 336 ------- 337 ```py 338 origin = Vec((1, 2, 3, 4)) 339 split = vec.split_off(2) 340 341 print(origin, split) # [1, 2], [3, 4] 342 ``` 343 344 Raises 345 ------ 346 `IndexError` 347 This method will raise if `at` > `len(self)` 348 """ 349 len_ = self.len() 350 if at > len_: 351 raise IndexError( 352 f"Index `at` ({at}) should be <= than len of vector ({len_}) " 353 ) from None 354 355 # Either the list is empty or uninit. 356 if not self._buf: 357 return self 358 359 split = self._buf[at:len_] 360 del self._buf[at:len_] 361 return Vec(split)
Split the vector off at the specified position.
Returns a new vector at the range of [at : len]
, leaving self
at [0: at]
.
if this vec is empty, self
is returned unchanged.
Example
origin = Vec((1, 2, 3, 4))
split = vec.split_off(2)
print(origin, split) # [1, 2], [3, 4]
Raises
IndexError
: This method will raise ifat
>len(self)
363 def truncate(self, size: int) -> None: 364 """Shortens the vec, keeping the first `size` elements and dropping the rest. 365 366 Example 367 ------- 368 ```py 369 vec = Vec([1,2,3]) 370 vec.truncate(1) 371 assert vec == [1] 372 ``` 373 """ 374 del self._buf[size:]
Shortens the vec, keeping the first size
elements and dropping the rest.
Example
vec = Vec([1,2,3])
vec.truncate(1)
assert vec == [1]
376 def retain(self, f: collections.Callable[[T], bool]) -> None: 377 """Retains only the elements specified by the predicate. 378 379 This operation occurs in-place without copying the original list. 380 381 Example 382 ------- 383 ```py 384 vec = Vec([1, 2, 3]) 385 vec.retain(lambda elem: elem > 1) 386 387 assert vec == [2, 3] 388 ``` 389 390 The impl for this method is very simple 391 ```py 392 for idx, e in enumerate(vec): 393 if not f(e): 394 del vec[idx] 395 ``` 396 """ 397 idx = 0 398 while idx < len(self._buf): 399 if not f(self._buf[idx]): 400 del self._buf[idx] 401 else: 402 idx += 1
Retains only the elements specified by the predicate.
This operation occurs in-place without copying the original list.
Example
vec = Vec([1, 2, 3])
vec.retain(lambda elem: elem > 1)
assert vec == [2, 3]
The impl for this method is very simple
for idx, e in enumerate(vec):
if not f(e):
del vec[idx]
404 def swap_remove(self, item: T) -> T: 405 """Remove the first appearance of `item` from this vector and return it. 406 407 Raises 408 ------ 409 `ValueError` 410 if `item` is not in this vector. 411 412 Example 413 ------- 414 ```py 415 vec = Vec(('a', 'b', 'c')) 416 element = vec.swap_remove('a') 417 assert vec == ['b', 'c'] and element == 'a' 418 ``` 419 """ 420 return self._buf.pop(self.index(item))
Remove the first appearance of item
from this vector and return it.
Raises
ValueError
: ifitem
is not in this vector.
Example
vec = Vec(('a', 'b', 'c'))
element = vec.swap_remove('a')
assert vec == ['b', 'c'] and element == 'a'
422 def push(self, item: T) -> None: 423 """Push an element at the end of the vector. 424 425 Example 426 ------- 427 ```py 428 vec = Vec() 429 vec.push(1) 430 431 assert vec == [1] 432 ``` 433 """ 434 if (cap := self._capacity) is not None and len(self._buf) == cap: 435 # max cap reached. 436 return 437 438 self._buf.append(item)
Push an element at the end of the vector.
Example
vec = Vec()
vec.push(1)
assert vec == [1]
440 def push_within_capacity(self, x: T) -> Result[None, T]: 441 """Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element. 442 443 Example 444 ------- 445 ```py 446 vec: Vec[int] = Vec.with_capacity(3) 447 for i in range(3): 448 match vec.push_within_capacity(i): 449 case Ok(_): 450 print("All good.") 451 case Err(elem): 452 print("Reached max cap :< can't push", elem) 453 ``` 454 455 Or you can also just call `Vec.push` and it will push if there's is sufficient capacity. 456 ```py 457 vec: Vec[int] = Vec.with_capacity(3) 458 459 vec.extend((1, 2, 3)) 460 vec.push(4) 461 462 assert vec.len() == 3 463 ``` 464 """ 465 if self.len() == self._capacity: 466 return _result.Err(x) 467 468 self._buf.append(x) 469 return _result.Ok(None)
Appends an element if there is sufficient spare capacity, otherwise an error is returned with the element.
Example
vec: Vec[int] = Vec.with_capacity(3)
for i in range(3):
match vec.push_within_capacity(i):
case Ok(_):
print("All good.")
case Err(elem):
print("Reached max cap :< can't push", elem)
Or you can also just call Vec.push
and it will push if there's is sufficient capacity.
vec: Vec[int] = Vec.with_capacity(3)
vec.extend((1, 2, 3))
vec.push(4)
assert vec.len() == 3
471 def reserve(self, additional: int) -> None: 472 """Reserves capacity for at least additional more elements to be inserted in the given Vec<T>. 473 474 Example 475 ------- 476 ```py 477 vec = Vec.with_capacity(3) 478 479 for i in range(3): 480 vec.push(i) 481 482 match vec.push_within_capacity(4): 483 case Ok(_): 484 print("Pushed 4 successfully.") 485 case Err(elem): 486 vec.reserve(1) # Reserve capacity for 1 more element. 487 vec.push(4) # Now we can push 4. 488 ``` 489 """ 490 if self._capacity is not None: 491 self._capacity += additional
Reserves capacity for at least additional more elements to be inserted in the given Vec
Example
vec = Vec.with_capacity(3)
for i in range(3):
vec.push(i)
match vec.push_within_capacity(4):
case Ok(_):
print("Pushed 4 successfully.")
case Err(elem):
vec.reserve(1) # Reserve capacity for 1 more element.
vec.push(4) # Now we can push 4.
493 def shrink_to_fit(self) -> None: 494 """Shrink the capacity of this `Vec` to match its length. 495 496 If `self` is initialized with no capacity, This is a `NOP`. 497 498 Example 499 ------- 500 ```py 501 s = Vec([1, 2, 3, 4]) 502 503 s.reserve(100) 504 s.shrink_to_fit() 505 assert s.capacity() == 4 506 ``` 507 """ 508 if self._capacity is None: 509 return 510 511 # The capacity is never less than the length. 512 self._capacity = min(self.__len__(), self._capacity)
Shrink the capacity of this Vec
to match its length.
If self
is initialized with no capacity, This is a NOP
.
Example
s = Vec([1, 2, 3, 4])
s.reserve(100)
s.shrink_to_fit()
assert s.capacity() == 4
514 def shrink_to(self, min_capacity: int) -> None: 515 """Shrink the capacity of this `Vec` to a minimum specified capacity. 516 517 If `self` is initialized with no capacity or the current capacity is less than the lower limit, 518 This is a `NOP`. 519 520 Example 521 ------- 522 ```py 523 vec = Vec.with_capacity(10) 524 vec.extend([1, 2, 3]) 525 assert vec.capacity() >= 10 526 vec.shrink_to(4) 527 assert vec.capacity() >= 4 528 vec.shrink_to(0) 529 ``` 530 """ 531 if self._capacity is None or self._capacity <= min_capacity: 532 return 533 534 # Ensure the capacity is not reduced below the current length of the vector. 535 self._capacity = max(self.__len__(), min_capacity)
Shrink the capacity of this Vec
to a minimum specified capacity.
If self
is initialized with no capacity or the current capacity is less than the lower limit,
This is a NOP
.
Example
vec = Vec.with_capacity(10)
vec.extend([1, 2, 3])
assert vec.capacity() >= 10
vec.shrink_to(4)
assert vec.capacity() >= 4
vec.shrink_to(0)
541 def append(self, value: T) -> None: 542 """Append an element at the end of the vector. 543 544 An alias to `Vec.push`. 545 """ 546 self._buf.append(value)
Append an element at the end of the vector.
An alias to Vec.push
.
548 def insert(self, index: int, value: T) -> None: 549 """Insert an element at the position `index`. 550 551 Example 552 -------- 553 ```py 554 vec = Vec((2, 3)) 555 vec.insert(0, 1) 556 assert vec == [1, 2, 3] 557 ``` 558 """ 559 self[index] = value
Insert an element at the position index
.
Example
vec = Vec((2, 3))
vec.insert(0, 1)
assert vec == [1, 2, 3]
561 def pop(self, index: int = -1) -> _option.Option[T]: 562 """Removes the last element from the vector and returns it, or `None` if it is empty. 563 564 Example 565 ------- 566 ```py 567 vec = Vec((1, 2, 3)) 568 assert vec.pop() == Some(3) 569 assert vec == [1, 2] 570 ``` 571 """ 572 if not self._buf: 573 return _option.NOTHING 574 575 return _option.Some(self._buf.pop(index))
Removes the last element from the vector and returns it, or None
if it is empty.
Example
vec = Vec((1, 2, 3))
assert vec.pop() == Some(3)
assert vec == [1, 2]
577 def pop_if(self, pred: collections.Callable[[T], bool]) -> _option.Option[T]: 578 """Removes the last element from a vector and returns it if `f` returns `True`, 579 or `None` if it is empty. 580 581 Example 582 ------- 583 ```py 584 vec = Vec((1, 2, 3)) 585 assert vec.pop_if(lambda num: num * 2 == 6) == Some(3) 586 assert vec == [1, 2] 587 ``` 588 """ 589 if not self._buf: 590 return _option.NOTHING 591 592 if pred(self[-1]): 593 return self.pop() 594 595 return _option.NOTHING
Removes the last element from a vector and returns it if f
returns True
,
or None
if it is empty.
Example
vec = Vec((1, 2, 3))
assert vec.pop_if(lambda num: num * 2 == 6) == Some(3)
assert vec == [1, 2]
597 def dedup(self) -> None: 598 """Removes consecutive repeated elements in the vector according to their `__eq__` 599 implementation. 600 601 Example 602 ------- 603 ```py 604 vec = Vec([1, 2, 2, 3, 2]) 605 vec.dedup() 606 assert vec == [1, 2, 3, 2] 607 """ 608 self.dedup_by(lambda a, b: a == b)
Removes consecutive repeated elements in the vector according to their __eq__
implementation.
Example
```py vec = Vec([1, 2, 2, 3, 2]) vec.dedup() assert vec == [1, 2, 3, 2]
610 def dedup_by(self, same_bucket: collections.Callable[[T, T], bool]) -> None: 611 """Removes all but the first of consecutive elements in the vector satisfying a given equality. 612 613 Example 614 ------- 615 ```py 616 vec = Vec(["foo", "bar", "Bar", "baz", "bar"]) 617 vec.dedup_by(lambda a, b: a.lower() == b.lower()) 618 assert vec == ["foo", "bar", "baz", "bar"] 619 ``` 620 621 Only remove consecutive duplicates. 622 ```py 623 vec = Vec([1, 2, 2, 3, 4, 1]) 624 vec.dedup_by(lambda a, b: a == b) 625 # The final 1 is not adjacent to the first 1, so it is not removed. 626 assert vec == [1, 2, 3, 4, 1] 627 ``` 628 """ 629 630 if not self._buf or (len_ := len(self._buf)) <= 1: 631 return 632 633 idx = 1 634 while idx < len_: 635 if same_bucket(self._buf[idx], self._buf[idx - 1]): 636 del self._buf[idx] 637 len_ -= 1 638 else: 639 idx += 1
Removes all but the first of consecutive elements in the vector satisfying a given equality.
Example
vec = Vec(["foo", "bar", "Bar", "baz", "bar"])
vec.dedup_by(lambda a, b: a.lower() == b.lower())
assert vec == ["foo", "bar", "baz", "bar"]
Only remove consecutive duplicates.
vec = Vec([1, 2, 2, 3, 4, 1])
vec.dedup_by(lambda a, b: a == b)
# The final 1 is not adjacent to the first 1, so it is not removed.
assert vec == [1, 2, 3, 4, 1]
641 def remove(self, item: T) -> None: 642 """Remove the first appearance of `item` from this vector. 643 644 Example 645 ------- 646 ```py 647 vec = Vec(('a', 'b', 'c')) 648 vec.remove('a') 649 assert vec == ['b', 'c'] 650 ``` 651 """ 652 self._buf.remove(item)
Remove the first appearance of item
from this vector.
Example
vec = Vec(('a', 'b', 'c'))
vec.remove('a')
assert vec == ['b', 'c']
654 def extend(self, iterable: collections.Iterable[T]) -> None: 655 """Extend this vector from another iterable. 656 657 Example 658 ------- 659 ```py 660 vec = Vec((1, 2, 3)) 661 vec.extend((4, 5, 6)) 662 663 assert vec == [1, 2, 3, 4, 5, 6] 664 ``` 665 """ 666 self._buf.extend(iterable)
Extend this vector from another iterable.
Example
vec = Vec((1, 2, 3))
vec.extend((4, 5, 6))
assert vec == [1, 2, 3, 4, 5, 6]
668 def copy(self) -> Vec[T]: 669 """Copy all elements of `self` into a new vector. 670 671 Example 672 ------- 673 ```py 674 original = Vec((1,2,3)) 675 copy = original.copy() 676 copy.push(4) 677 678 print(original) # [1, 2, 3] 679 ``` 680 """ 681 return Vec(self._buf[:])
Copy all elements of self
into a new vector.
Example
original = Vec((1,2,3))
copy = original.copy()
copy.push(4)
print(original) # [1, 2, 3]
683 def clear(self) -> None: 684 """Clear all elements of this vector. 685 686 Example 687 ------- 688 ```py 689 vec = Vec((1,2,3)) 690 vec.clear() 691 assert vec.len() == 0 692 ``` 693 """ 694 self._buf.clear()
Clear all elements of this vector.
Example
vec = Vec((1,2,3))
vec.clear()
assert vec.len() == 0
696 def sort( 697 self, 698 *, 699 key: collections.Callable[[T], SupportsRichComparison] | None = None, 700 reverse: bool = False, 701 ) -> None: 702 """This method sorts the list in place, using only < comparisons between items. 703 704 Example 705 ------- 706 ```py 707 vec = Vec((2, 1, 3)) 708 vec.sort() 709 assert vec == [1, 2, 3] 710 ``` 711 """ 712 # key can be `None` here just fine, idk why pyright is complaining. 713 self._buf.sort(key=key, reverse=reverse) # pyright: ignore
This method sorts the list in place, using only < comparisons between items.
Example
vec = Vec((2, 1, 3))
vec.sort()
assert vec == [1, 2, 3]
715 def count(self, item: T) -> int: 716 """Return the number of occurrences of `item` in the vec. 717 718 `0` is returned if the vector is empty or hasn't been initialized, as well if them item not found. 719 720 Example 721 -------- 722 ```py 723 vec = Vec((1, 2, 3, 3)) 724 assert vec.count(3) == 2 725 ``` 726 """ 727 return self._buf.count(item)
Return the number of occurrences of item
in the vec.
0
is returned if the vector is empty or hasn't been initialized, as well if them item not found.
Example
vec = Vec((1, 2, 3, 3))
assert vec.count(3) == 2
Inherited Members
68@rustc_diagnostic_item("Error") 69@typing.runtime_checkable 70class Error(ToString, typing.Protocol): 71 """`Error` is an interface usually used for values that returns `sain.Result[T, E]` 72 73 where `E` is an implementation of this interface. 74 75 Example 76 ------- 77 ```py 78 import requests 79 import http 80 from dataclasses import dataclass 81 82 from sain import Error 83 from sain import Result, Ok, Err 84 85 # an http error. 86 @dataclass 87 class HTTPError(Error): 88 response: requests.Response 89 kind: http.HTTPStatus 90 message: str = "" 91 92 def description(self) -> str: 93 return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}" 94 95 # A simple request that handles [404] responses. 96 def request(url: str, uid: int) -> Result[requests.Response, HTTPError]: 97 response = requests.get(url, json={"user_id": uid}) 98 if response.status_code == 404: 99 return Err( 100 HTTPError( 101 response, 102 kind=http.HTTPStatus.NOT_FOUND, 103 message=f"Resource not found for user_id {uid}", 104 ) 105 ) 106 return Ok(response) 107 108 # Execute the request 109 match request("some-url.com", 0): 110 case Ok(response): 111 # Deal with the response 112 ... 113 case Err(why): 114 # Deal with the error. 115 print(why) 116 117 ``` 118 """ 119 120 __slots__ = ("message",) 121 122 def __init__(self, message: str = "") -> None: 123 self.message = message 124 """A basic error message.""" 125 126 def source(self) -> Option[type[Error]]: 127 """The source of this error, if any.""" 128 return _option.NOTHING 129 130 def description(self) -> str: 131 """Context for this error.""" 132 return "" 133 134 def to_string(self) -> str: 135 return self.__repr__() 136 137 def __repr__(self) -> str: 138 source = None if (src := self.source()).is_none() else src 139 return ( 140 f'{type(self).__qualname__}(message: "{self.message}, source: {source!r})' 141 ) 142 143 def __str__(self) -> str: 144 return self.message 145 146 # An error is always falsy. 147 def __bool__(self) -> typing.Literal[False]: 148 return False
Error
is an interface usually used for values that returns sain.Result[T, E]
where E
is an implementation of this interface.
Example
import requests
import http
from dataclasses import dataclass
from sain import Error
from sain import Result, Ok, Err
# an http error.
@dataclass
class HTTPError(Error):
response: requests.Response
kind: http.HTTPStatus
message: str = ""
def description(self) -> str:
return f"HTTP Error [{self.response.status_code}, {self.kind}] for {self.response.url}"
# A simple request that handles [404] responses.
def request(url: str, uid: int) -> Result[requests.Response, HTTPError]:
response = requests.get(url, json={"user_id": uid})
if response.status_code == 404:
return Err(
HTTPError(
response,
kind=http.HTTPStatus.NOT_FOUND,
message=f"Resource not found for user_id {uid}",
)
)
return Ok(response)
# Execute the request
match request("some-url.com", 0):
case Ok(response):
# Deal with the response
...
case Err(why):
# Deal with the error.
print(why)
Implementations
This class implements Error in Rust.
126 def source(self) -> Option[type[Error]]: 127 """The source of this error, if any.""" 128 return _option.NOTHING
The source of this error, if any.
61@typing.final 62class Box(typing.Generic[T]): 63 """The box object for expiring data. not thread-safe. 64 65 A box is an object that contains a value of type `T` which expires it after the given amount of time, 66 The box won't start expiring the data until its first access with `Box.get` method. 67 68 Example 69 ------- 70 ```py 71 # Initializing a box doesn't mean it started expiring. instead, 72 # getting the value the first time will start the process. 73 cache: dict[str, Box[int]] = {"sora": Box(999, timedelta(seconds=5)} 74 75 # first start expiring here. 76 cache["sora"].get().unwrap() 77 time.sleep(6) 78 assert cache["sora"].has_expired() 79 ``` 80 """ 81 82 __slots__ = ("_inner", "_expire_in", "_on_expire", "_mono") 83 84 def __init__(self, value: T, expire_in: int | float | datetime.timedelta) -> None: 85 if isinstance(expire_in, datetime.timedelta): 86 expire_in = expire_in.total_seconds() 87 else: 88 expire_in = float(expire_in) 89 90 if expire_in <= 0: 91 raise ValueError("expire_in must be more than 0 seconds.") 92 93 # We set the last call on the first access to the value. 94 self._mono: float | None = None 95 self._inner: Option[T] = option.Some(value) 96 self._on_expire: collections.Callable[[T], typing.Any] | None = None 97 self._expire_in = expire_in 98 99 @property 100 def has_expired(self) -> bool: 101 """Returns True if the value has expired.""" 102 # return self._mono is not None and not self._expire_in <= ( 103 # time.monotonic() - self._mono 104 # ) 105 return self._mono is not None and ( 106 not self._mono or self._expire_in <= (time.monotonic() - self._mono) 107 ) 108 109 def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self: 110 """Set a callback that will be invoked when this value gets expired. 111 112 Both async and sync callbacks are supported. 113 114 Example 115 ------- 116 ```py 117 async def sink(message: str) -> None: 118 await client.create_message(message) 119 print("Sinked", message) 120 121 box = Box("bluh", 5).on_expire(sink) 122 123 while box.get().is_some(): 124 time.sleep(5) 125 ``` 126 First `.get` call on an expired box, the `sink` callback will be invoked, 127 also the inner value will be set to `Some(None)`. 128 129 After 5 seconds. 130 ```py 131 assert box.get() == Some("bluh") # This last call invokes the callback. 132 # Sinked bluh 133 assert box.get().is_none() 134 ``` 135 """ 136 self._on_expire = callback 137 return self 138 139 def remaining(self) -> float: 140 """Returns when this box will expire in seconds. 141 142 Example 143 -------- 144 ```py 145 jogo = Box("jogo", 3) 146 assert jogo.get().unwrap() == "jogo" 147 148 time.sleep(1) 149 assert jogo.remaining() == 2 150 ``` 151 """ 152 if not self._mono: 153 return 0.0 154 155 return math.floor( 156 (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99 157 ) 158 159 def get(self) -> Option[T]: 160 """Get the contained value if it was not expired, otherwise `Some(None)` is returned. 161 162 Example 163 ------- 164 ```py 165 pizza = Box("pizza", timedelta(days=1)) 166 167 while not pizza.get().is_none(): 168 # Do stuff with the value while its not expired. 169 170 # After 1 day. 171 assert pizza.get().is_none() 172 ``` 173 """ 174 if self.has_expired: 175 if self._on_expire is not None: 176 with warnings.catch_warnings(): 177 # ignore the warnings from `unwrap_unchecked`. 178 warnings.simplefilter("ignore", category=ub_checks) 179 try: 180 if asyncio.iscoroutinefunction(self._on_expire): 181 futures.loop().run_until_complete( 182 self._on_expire(self._inner.unwrap_unchecked()) 183 ) 184 else: 185 self._on_expire(self._inner.unwrap_unchecked()) 186 finally: 187 self._on_expire = None 188 189 self._inner = option.NOTHING 190 self._mono = None 191 # SAFETY: The value is expired, therefore we always return None. 192 return option.NOTHING 193 194 if self._mono is None: 195 self._mono = time.monotonic() 196 197 return self._inner 198 199 def __repr__(self) -> str: 200 return f"Box(value: {self._inner}, expired: {self.has_expired})" 201 202 __str__ = __repr__ 203 204 def __hash__(self) -> int: 205 return hash(self._inner) 206 207 def __bool__(self) -> bool: 208 return not self.has_expired
The box object for expiring data. not thread-safe.
A box is an object that contains a value of type T
which expires it after the given amount of time,
The box won't start expiring the data until its first access with Box.get
method.
Example
# Initializing a box doesn't mean it started expiring. instead,
# getting the value the first time will start the process.
cache: dict[str, Box[int]] = {"sora": Box(999, timedelta(seconds=5)}
# first start expiring here.
cache["sora"].get().unwrap()
time.sleep(6)
assert cache["sora"].has_expired()
84 def __init__(self, value: T, expire_in: int | float | datetime.timedelta) -> None: 85 if isinstance(expire_in, datetime.timedelta): 86 expire_in = expire_in.total_seconds() 87 else: 88 expire_in = float(expire_in) 89 90 if expire_in <= 0: 91 raise ValueError("expire_in must be more than 0 seconds.") 92 93 # We set the last call on the first access to the value. 94 self._mono: float | None = None 95 self._inner: Option[T] = option.Some(value) 96 self._on_expire: collections.Callable[[T], typing.Any] | None = None 97 self._expire_in = expire_in
99 @property 100 def has_expired(self) -> bool: 101 """Returns True if the value has expired.""" 102 # return self._mono is not None and not self._expire_in <= ( 103 # time.monotonic() - self._mono 104 # ) 105 return self._mono is not None and ( 106 not self._mono or self._expire_in <= (time.monotonic() - self._mono) 107 )
Returns True if the value has expired.
109 def on_expire(self, callback: collections.Callable[[T], typing.Any]) -> Self: 110 """Set a callback that will be invoked when this value gets expired. 111 112 Both async and sync callbacks are supported. 113 114 Example 115 ------- 116 ```py 117 async def sink(message: str) -> None: 118 await client.create_message(message) 119 print("Sinked", message) 120 121 box = Box("bluh", 5).on_expire(sink) 122 123 while box.get().is_some(): 124 time.sleep(5) 125 ``` 126 First `.get` call on an expired box, the `sink` callback will be invoked, 127 also the inner value will be set to `Some(None)`. 128 129 After 5 seconds. 130 ```py 131 assert box.get() == Some("bluh") # This last call invokes the callback. 132 # Sinked bluh 133 assert box.get().is_none() 134 ``` 135 """ 136 self._on_expire = callback 137 return self
Set a callback that will be invoked when this value gets expired.
Both async and sync callbacks are supported.
Example
async def sink(message: str) -> None:
await client.create_message(message)
print("Sinked", message)
box = Box("bluh", 5).on_expire(sink)
while box.get().is_some():
time.sleep(5)
First .get
call on an expired box, the sink
callback will be invoked,
also the inner value will be set to Some(None)
.
After 5 seconds.
assert box.get() == Some("bluh") # This last call invokes the callback.
# Sinked bluh
assert box.get().is_none()
139 def remaining(self) -> float: 140 """Returns when this box will expire in seconds. 141 142 Example 143 -------- 144 ```py 145 jogo = Box("jogo", 3) 146 assert jogo.get().unwrap() == "jogo" 147 148 time.sleep(1) 149 assert jogo.remaining() == 2 150 ``` 151 """ 152 if not self._mono: 153 return 0.0 154 155 return math.floor( 156 (self._expire_in - (time.monotonic() - self._mono) + 1) * 0.99 157 )
Returns when this box will expire in seconds.
Example
jogo = Box("jogo", 3)
assert jogo.get().unwrap() == "jogo"
time.sleep(1)
assert jogo.remaining() == 2
159 def get(self) -> Option[T]: 160 """Get the contained value if it was not expired, otherwise `Some(None)` is returned. 161 162 Example 163 ------- 164 ```py 165 pizza = Box("pizza", timedelta(days=1)) 166 167 while not pizza.get().is_none(): 168 # Do stuff with the value while its not expired. 169 170 # After 1 day. 171 assert pizza.get().is_none() 172 ``` 173 """ 174 if self.has_expired: 175 if self._on_expire is not None: 176 with warnings.catch_warnings(): 177 # ignore the warnings from `unwrap_unchecked`. 178 warnings.simplefilter("ignore", category=ub_checks) 179 try: 180 if asyncio.iscoroutinefunction(self._on_expire): 181 futures.loop().run_until_complete( 182 self._on_expire(self._inner.unwrap_unchecked()) 183 ) 184 else: 185 self._on_expire(self._inner.unwrap_unchecked()) 186 finally: 187 self._on_expire = None 188 189 self._inner = option.NOTHING 190 self._mono = None 191 # SAFETY: The value is expired, therefore we always return None. 192 return option.NOTHING 193 194 if self._mono is None: 195 self._mono = time.monotonic() 196 197 return self._inner
Get the contained value if it was not expired, otherwise Some(None)
is returned.
Example
pizza = Box("pizza", timedelta(days=1))
while not pizza.get().is_none():
# Do stuff with the value while its not expired.
# After 1 day.
assert pizza.get().is_none()
148@rustc_diagnostic_item("From") 149@typing.runtime_checkable 150class From(typing.Protocol[T_co]): 151 """Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into. 152 153 As the Rust documentation says, One should always prefer implementing From over Into 154 because implementing From automatically provides one with an implementation of Into. 155 156 But there's no such thing in Python, as it's impossible to auto-impl `Into<T>` for all types 157 that impl `From<T>`. 158 159 So for the sake of simplicity, You should implement whichever interface you want deal with, 160 Or simply, implement both as the same time. 161 162 Example 163 ------- 164 ```py 165 @dataclass 166 class Id(From[str]): 167 value: int 168 169 @classmethod 170 def from_value(cls, value: str) -> Self: 171 return cls(value=int(value)) 172 173 ``` 174 """ 175 176 __slots__ = () 177 178 @classmethod 179 def from_value(cls, value: T_co) -> Self: 180 """Perform the conversion.""" 181 raise NotImplementedError
Used to do value-to-value conversions while consuming the input value. It is the reciprocal of Into.
As the Rust documentation says, One should always prefer implementing From over Into because implementing From automatically provides one with an implementation of Into.
But there's no such thing in Python, as it's impossible to auto-impl Into<T>
for all types
that impl From<T>
.
So for the sake of simplicity, You should implement whichever interface you want deal with, Or simply, implement both as the same time.
Example
@dataclass
class Id(From[str]):
value: int
@classmethod
def from_value(cls, value: str) -> Self:
return cls(value=int(value))
Implementations
This class implements From in Rust.
1945def _no_init_or_replace_init(self, *args, **kwargs): 1946 cls = type(self) 1947 1948 if cls._is_protocol: 1949 raise TypeError('Protocols cannot be instantiated') 1950 1951 # Already using a custom `__init__`. No need to calculate correct 1952 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1953 if cls.__init__ is not _no_init_or_replace_init: 1954 return 1955 1956 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1957 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1958 # searches for a proper new `__init__` in the MRO. The new `__init__` 1959 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1960 # instantiation of the protocol subclass will thus use the new 1961 # `__init__` and no longer call `_no_init_or_replace_init`. 1962 for base in cls.__mro__: 1963 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1964 if init is not _no_init_or_replace_init: 1965 cls.__init__ = init 1966 break 1967 else: 1968 # should not happen 1969 cls.__init__ = object.__init__ 1970 1971 cls.__init__(self, *args, **kwargs)
184@rustc_diagnostic_item("TryFrom") 185@typing.runtime_checkable 186class TryFrom(typing.Protocol[T_co, E]): 187 """Simple and safe type conversions that may fail in a controlled way under some circumstances. 188 It is the reciprocal of `TryInto`. 189 190 It is useful to implement this when you know that the conversion may fail in some way. 191 192 Generic Implementations 193 ------------------- 194 This interface takes two type arguments, and return `Result<Self, E>` 195 196 * `T`: Which's the first generic `T` is the type that's being converted from. 197 * `E`: If the conversion fails in a way, this is what will return as the error. 198 * `Self`: Which's the instance of the class that is being converted into. 199 200 Example 201 ------- 202 ```py 203 @dataclass 204 class Id(TryFrom[str, str]): 205 value: int 206 207 @classmethod 208 def try_from(cls, value: str) -> Result[Self, str]: 209 if not value.isnumeric(): 210 # NaN 211 return Err(f"Couldn't convert: {value} to self") 212 # otherwise convert it to an Id instance. 213 return Ok(value=cls(int(value))) 214 ``` 215 """ 216 217 __slots__ = () 218 219 @classmethod 220 def try_from(cls, value: T_co) -> Result[Self, E]: 221 """Perform the conversion.""" 222 raise NotImplementedError
Simple and safe type conversions that may fail in a controlled way under some circumstances.
It is the reciprocal of TryInto
.
It is useful to implement this when you know that the conversion may fail in some way.
Generic Implementations
This interface takes two type arguments, and return Result<Self, E>
T
: Which's the first genericT
is the type that's being converted from.E
: If the conversion fails in a way, this is what will return as the error.Self
: Which's the instance of the class that is being converted into.
Example
@dataclass
class Id(TryFrom[str, str]):
value: int
@classmethod
def try_from(cls, value: str) -> Result[Self, str]:
if not value.isnumeric():
# NaN
return Err(f"Couldn't convert: {value} to self")
# otherwise convert it to an Id instance.
return Ok(value=cls(int(value)))
Implementations
This class implements TryFrom in Rust.
1945def _no_init_or_replace_init(self, *args, **kwargs): 1946 cls = type(self) 1947 1948 if cls._is_protocol: 1949 raise TypeError('Protocols cannot be instantiated') 1950 1951 # Already using a custom `__init__`. No need to calculate correct 1952 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1953 if cls.__init__ is not _no_init_or_replace_init: 1954 return 1955 1956 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1957 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1958 # searches for a proper new `__init__` in the MRO. The new `__init__` 1959 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1960 # instantiation of the protocol subclass will thus use the new 1961 # `__init__` and no longer call `_no_init_or_replace_init`. 1962 for base in cls.__mro__: 1963 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1964 if init is not _no_init_or_replace_init: 1965 cls.__init__ = init 1966 break 1967 else: 1968 # should not happen 1969 cls.__init__ = object.__init__ 1970 1971 cls.__init__(self, *args, **kwargs)
225@rustc_diagnostic_item("TryFrom") 226@typing.runtime_checkable 227class Into(typing.Protocol[T_cov]): 228 """Conversion from `self`, which may or may not be expensive. 229 230 Example 231 ------- 232 ```py 233 @dataclass 234 class Id(Into[str]): 235 value: int 236 237 def into(self) -> str: 238 return str(self.value) 239 ``` 240 """ 241 242 __slots__ = () 243 244 def into(self) -> T_cov: 245 """Perform the conversion.""" 246 raise NotImplementedError
Conversion from self
, which may or may not be expensive.
Example
@dataclass
class Id(Into[str]):
value: int
def into(self) -> str:
return str(self.value)
Implementations
This class implements TryFrom in Rust.
1945def _no_init_or_replace_init(self, *args, **kwargs): 1946 cls = type(self) 1947 1948 if cls._is_protocol: 1949 raise TypeError('Protocols cannot be instantiated') 1950 1951 # Already using a custom `__init__`. No need to calculate correct 1952 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1953 if cls.__init__ is not _no_init_or_replace_init: 1954 return 1955 1956 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1957 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1958 # searches for a proper new `__init__` in the MRO. The new `__init__` 1959 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1960 # instantiation of the protocol subclass will thus use the new 1961 # `__init__` and no longer call `_no_init_or_replace_init`. 1962 for base in cls.__mro__: 1963 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1964 if init is not _no_init_or_replace_init: 1965 cls.__init__ = init 1966 break 1967 else: 1968 # should not happen 1969 cls.__init__ = object.__init__ 1970 1971 cls.__init__(self, *args, **kwargs)
249@rustc_diagnostic_item("TryInto") 250@typing.runtime_checkable 251class TryInto(typing.Protocol[T, E]): 252 """An attempted conversion from `self`, which may or may not be expensive. 253 254 It is useful to implement this when you know that the conversion may fail in some way. 255 256 Generic Implementations 257 ------------------- 258 This interface takes two type arguments, and return `Result<T, E>` 259 260 * `T`: The first generic `T` is the type that's being converted into. 261 * `E`: If the conversion fails in a way, this is what will return as the error. 262 263 Example 264 ------- 265 ```py 266 @dataclass 267 class Id(TryInto[int, str]): 268 value: str 269 270 def try_into(self) -> Result[int, str]: 271 if not self.value.isnumeric(): 272 return Err(f"{self.value} is not a number...") 273 return Ok(int(self.value)) 274 ``` 275 """ 276 277 __slots__ = () 278 279 def try_into(self) -> Result[T, E]: 280 """Perform the conversion.""" 281 raise NotImplementedError
An attempted conversion from self
, which may or may not be expensive.
It is useful to implement this when you know that the conversion may fail in some way.
Generic Implementations
This interface takes two type arguments, and return Result<T, E>
T
: The first genericT
is the type that's being converted into.E
: If the conversion fails in a way, this is what will return as the error.
Example
@dataclass
class Id(TryInto[int, str]):
value: str
def try_into(self) -> Result[int, str]:
if not self.value.isnumeric():
return Err(f"{self.value} is not a number...")
return Ok(int(self.value))
Implementations
This class implements TryInto in Rust.
1945def _no_init_or_replace_init(self, *args, **kwargs): 1946 cls = type(self) 1947 1948 if cls._is_protocol: 1949 raise TypeError('Protocols cannot be instantiated') 1950 1951 # Already using a custom `__init__`. No need to calculate correct 1952 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1953 if cls.__init__ is not _no_init_or_replace_init: 1954 return 1955 1956 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1957 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1958 # searches for a proper new `__init__` in the MRO. The new `__init__` 1959 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1960 # instantiation of the protocol subclass will thus use the new 1961 # `__init__` and no longer call `_no_init_or_replace_init`. 1962 for base in cls.__mro__: 1963 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1964 if init is not _no_init_or_replace_init: 1965 cls.__init__ = init 1966 break 1967 else: 1968 # should not happen 1969 cls.__init__ = object.__init__ 1970 1971 cls.__init__(self, *args, **kwargs)
284@rustc_diagnostic_item("ToString") 285@typing.runtime_checkable 286class ToString(typing.Protocol): 287 """A trait for explicitly converting a value to a `str`. 288 289 Example 290 ------- 291 ```py 292 class Value[T: bytes](ToString): 293 buffer: T 294 295 def to_string(self) -> str: 296 return self.buffer.decode("utf-8") 297 ``` 298 """ 299 300 __slots__ = () 301 302 def to_string(self) -> str: 303 """Converts the given value to a `str`. 304 305 Example 306 -------- 307 ```py 308 i = 5 # assume `int` implements `ToString` 309 five = "5" 310 assert five == i.to_string() 311 ``` 312 """ 313 raise NotImplementedError 314 315 def __str__(self) -> str: 316 return self.to_string()
A trait for explicitly converting a value to a str
.
Example
class Value[T: bytes](ToString):
buffer: T
def to_string(self) -> str:
return self.buffer.decode("utf-8")
Implementations
This class implements ToString in Rust.
1945def _no_init_or_replace_init(self, *args, **kwargs): 1946 cls = type(self) 1947 1948 if cls._is_protocol: 1949 raise TypeError('Protocols cannot be instantiated') 1950 1951 # Already using a custom `__init__`. No need to calculate correct 1952 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1953 if cls.__init__ is not _no_init_or_replace_init: 1954 return 1955 1956 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1957 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1958 # searches for a proper new `__init__` in the MRO. The new `__init__` 1959 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1960 # instantiation of the protocol subclass will thus use the new 1961 # `__init__` and no longer call `_no_init_or_replace_init`. 1962 for base in cls.__mro__: 1963 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1964 if init is not _no_init_or_replace_init: 1965 cls.__init__ = init 1966 break 1967 else: 1968 # should not happen 1969 cls.__init__ = object.__init__ 1970 1971 cls.__init__(self, *args, **kwargs)
302 def to_string(self) -> str: 303 """Converts the given value to a `str`. 304 305 Example 306 -------- 307 ```py 308 i = 5 # assume `int` implements `ToString` 309 five = "5" 310 assert five == i.to_string() 311 ``` 312 """ 313 raise NotImplementedError
Converts the given value to a str
.
Example
i = 5 # assume `int` implements `ToString`
five = "5"
assert five == i.to_string()