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