Skip to documentation
SLOP

tiny.python.object.value

Reference tiny.python object value

Defined in object.

Every Python value the package handles is one tagged union: None, booleans and integers are held in the value itself, a string is a slice of bytes held elsewhere, and lists, tuples, dictionaries, ranges and the other objects are pointers to objects held elsewhere.

API (25)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callsobjectvalue
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstest sourcelib.python.src.object.valuetest: dictionary existing updates all...test sourcelib.python.src.object.valuetest: dictionary hashes equality whil...test sourcelib.python.src.object.valuetest: dictionary insertion failure le...object.Dictdeinit
Static calls · unresolved targets: 1 · external targets: 1.
Called byCallsNo direct callstest sourcelib.python.src.object.valuetest: dictionary existing updates all...test sourcelib.python.src.object.valuetest: dictionary hashes equality whil...test sourcelib.python.src.object.valuetest: dictionary insertion failure le...object.DictindexOf
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsNo direct callstest sourcelib.python.src.object.valuetest: dictionary existing updates all...test sourcelib.python.src.object.valuetest: dictionary hashes equality whil...test sourcelib.python.src.object.valuetest: dictionary insertion failure le...object.Dictput
Static calls · unresolved targets: 3 · external targets: 1.
Called byCallsNo direct callstest sourcelib.python.src.object.valuetest: dictionary hashes equality whil...object.DictremoveAt
Static calls · unresolved targets: 2 · external targets: 1.

Source: lib/python/src/object/root.zig:10

zig
pub const value = @import("value.zig");

Source: lib/python/src/object/value.zig

zig
//! Every Python value the package handles is one tagged union: `None`, booleans and integers are//! held in the value itself, a string is a slice of bytes held elsewhere, and lists, tuples,//! dictionaries, ranges and the other objects are pointers to objects held elsewhere.//!//! The virtual machine copies values between its stack, its variables and its containers at every//! step, so a value has to be small and cheap to copy. Python's rules for truth, equality and//! hashing have to hold across all of the types.//!//! Python treats `True` and `False` as the integers 1 and 0 in arithmetic, equality and hashing, so//! `True == 1` holds and a dictionary finds the key `1` under `True`. A dictionary has to keep//! insertion order and find keys by hash, and only values that cannot change may be keys. Two lists//! can hold the same items and still be different objects, so equality and identity are separate//! questions.//!//! The values follow the rules of the [Python 3.14 language//! reference](https://docs.python.org/3.14/reference/), and the file's tests check them: Python's//! truth rules, its integer reading of booleans, its equality for strings, lists, tuples and//! ranges, and equal hashes for equal keys.//!//! Python's integers grow without bound, and this package stores an integer in 128 signed bits, so//! arithmetic that leaves that range fails with `IntegerOverflow`. A string is a borrowed slice of//! UTF-8 bytes that points into the source text or into the heap. Length, iteration and slicing of//! a string count codepoints. A function value is the position of its body in the top-level chunk,//! and a builtin value is a tag that names one of nine builtin functions. A dictionary keeps its//! entries in an array in insertion order and maps each key to its entry's position in a separate//! hash table, so lookup goes by hash and iteration goes by insertion order. Removing a dictionary//! entry shifts every later entry down one position and updates each later entry's position in the//! hash table, so removal takes time linear in the number of later entries. `hash` uses Wyhash with//! a fixed seed, so a value hashes the same in every run. `hash` reads a boolean as its integer, so//! equal keys share a hash. Each iterator is a small record that points to what it walks and holds//! a position, with a flag for walking from the end.const std = @import("std");/// One Python value, as a tagged union of thirteen kinds. The virtual machine reads and writes/// values in every operation, and `Result.value` is one. `None`, booleans and integers live in the/// value itself, a string is a slice of bytes held elsewhere, and every other kind points to an/// object held elsewhere. The objects that the virtual machine makes live on its heap. Copying a/// value copies the pointer, so both copies refer to the same object.pub const Value = union(enum) {    /// Python's `None`, with no payload. `None` is false by Python's truth rules and equal only to    /// `None`.    none,    /// Python's `True` or `False`. A boolean counts as the integer 1 or 0 in arithmetic, equality,    /// ordering and hashing, so `True == 1` holds and a dictionary finds the key `1` under `True`.    /// A boolean is never the same object as an integer under `is`.    boolean: bool,    /// A Python integer, stored in 128 signed bits. Arithmetic that leaves that range fails with    /// `IntegerOverflow`.    integer: i128,    /// A Python string, as a slice of UTF-8 bytes that the value borrows. A string literal and its    /// slices with step 1 point into the source text, and a string the run built points into the    /// heap. Two strings are equal when their bytes are equal. Two strings are the same object    /// under `is` when they start at the same address and have the same length.    string: []const u8,    /// A Python function, as the position of its body in the function table of the top-level chunk.    /// The position names nothing once that chunk is freed.    function: usize,    /// One of the nine builtin functions, named by a `Builtin` tag.    builtin: Builtin,    /// A list or dictionary method bound to the object it was read from, as a pointer to a    /// `NativeMethod` on the heap.    method: *NativeMethod,    /// A pointer to a Python list. When the virtual machine made the list, the machine's heap owns    /// it.    list: *List,    /// A pointer to a Python tuple. When the virtual machine made the tuple, the machine's heap    /// owns it.    tuple: *Tuple,    /// A pointer to a Python dictionary. When the virtual machine made the dictionary, the    /// machine's heap owns it.    dict: *Dict,    /// A pointer to a view of a dictionary's keys, values or items.    view: *DictView,    /// A pointer to a Python range.    range: *Range,    /// A pointer to an iterator, whose position advances as it yields items.    iterator: *Iterator,    /// Returns whether the value is true by Python's truth rules, for `if`, `while`, `and`, `or`    /// and `not`. `None`, `False`, zero, and empty strings, lists, tuples, dictionaries, dictionary    /// views and ranges are false. A dictionary view is false when its dictionary is empty.    /// Functions, builtins, bound methods and iterators are always true.    pub fn truthy(self: Value) bool {        return switch (self) {            .none => false,            .boolean => |value| value,            .integer => |value| value != 0,            .string => |value| value.len != 0,            .function => true,            .builtin => true,            .method => true,            .list => |value| value.items.len != 0,            .tuple => |value| value.items.len != 0,            .dict => |value| value.entries.items.len != 0,            .view => |value| value.dict.entries.items.len != 0,            .range => |value| value.length != 0,            .iterator => true,        };    }    /// Returns the integer a value stands for: an integer's own value, 1 for `True`, 0 for `False`,    /// and `null` for every other kind. Arithmetic, indexing, `range`, `enumerate` and repetition    /// read their integer inputs through it.    pub fn integerLike(self: Value) ?i128 {        return switch (self) {            .integer => |value| value,            .boolean => |value| if (value) 1 else 0,            .none => null,            .string => null,            .function => null,            .builtin => null,            .method => null,            .list => null,            .tuple => null,            .dict => null,            .view => null,            .range => null,            .iterator => null,        };    }    /// Returns whether the value may be a dictionary key, so the virtual machine checks it before    /// every dictionary lookup and insertion. `None`, booleans, integers, strings, functions,    /// builtins, bound methods and ranges may be keys. A tuple may be a key when every item may.    /// Lists, dictionaries, dictionary views and iterators may never be keys. The check walks a    /// tuple item by item, so its cost grows with the tuple.    pub fn hashable(self: Value) bool {        return switch (self) {            .none, .boolean, .integer, .string, .function, .builtin, .method, .range => true,            .tuple => |value| sequenceHashable(value.items),            .list, .dict, .view, .iterator => false,        };    }    /// Returns whether two values are equal by Python's rules, for `==`, `!=`, membership tests and    /// dictionary lookups. Integers and booleans compare by their integer value. Strings compare    /// their bytes, and lists and tuples compare item by item. Two dictionaries are equal when they    /// hold equal values under the same keys, in any order. Two ranges are equal when they yield    /// the same elements. Keys views compare their dictionaries' keys, items views compare their    /// dictionaries, and a values view equals only itself. Two bound methods are equal when they    /// have the same method and the same receiver. Functions and builtins compare their positions    /// and tags, and iterators are equal only to themselves. Values of unrelated kinds are unequal,    /// and the comparison returns no error. Comparing a list with itself returns true before    /// comparing its items. Comparing a tuple with itself returns true before comparing its items.    /// A list containing itself therefore equals itself, including when it holds a tuple containing    /// that same list. Two distinct lists or dictionaries that each contain themselves recurse    /// until the native stack overflows when comparison reaches those self-references because    /// comparison has no depth bound.    pub fn eql(self: Value, other: Value) bool {        if (self.integerLike()) |left| {            if (other.integerLike()) |right| return left == right;        }        return switch (self) {            .none => other == .none,            .boolean => false,            .integer => false,            .string => |left| switch (other) {                .string => |right| std.mem.eql(u8, left, right),                else => false,            },            .function => |left| switch (other) {                .function => |right| left == right,                else => false,            },            .builtin => |left| switch (other) {                .builtin => |right| left == right,                else => false,            },            .method => |left| switch (other) {                .method => |right| nativeMethodsEqual(left, right),                else => false,            },            .list => |left| switch (other) {                .list => |right| listsEqual(left, right),                else => false,            },            .tuple => |left| switch (other) {                .tuple => |right| left == right or sequencesEqual(left.items, right.items),                else => false,            },            .dict => |left| switch (other) {                .dict => |right| dictsEqual(left, right),                else => false,            },            .view => |left| switch (other) {                .view => |right| dictViewsEqual(left, right),                else => false,            },            .range => |left| switch (other) {                .range => |right| rangesEqual(left, right),                else => false,            },            .iterator => |left| switch (other) {                .iterator => |right| left == right,                else => false,            },        };    }    /// Returns a 64-bit Wyhash of the value, with a fixed seed, so a value hashes the same in every    /// run. The dictionary's hash table hashes keys with it. Equal values hash equally: an integer    /// and the boolean with its value share a hash, and so do equal ranges and equal tuples. A    /// bound method hashes its method and its receiver's address. The function asserts that the    /// value is hashable, so the caller checks `hashable` first.    pub fn hash(self: Value) u64 {        std.debug.assert(self.hashable());        var hasher = std.hash.Wyhash.init(0x5059_5448_4f4e_5641);        hashInto(&hasher, self);        return hasher.final();    }};/// The builtin functions that the package provides, one tag each, named after the function: `dict`,/// `enumerate`, `iter`, `len`, `list`, `next`, `range`, `reversed` and `tuple`. The virtual machine/// pushes one when a name matches no local or global variable. Calling a builtin runs it inside the/// virtual machine and pushes its result.pub const Builtin = enum {    dict,    enumerate,    iter,    len,    list,    next,    range,    reversed,    tuple,};/// A list or dictionary method bound to the object it was read from, as a tagged union with one tag/// per method. Reading a method of a list or dictionary makes one, and calling it runs the method./// Each tag's payload points to that object. Each tag is named after the object's type and the/// method: `dict_clear`, `dict_copy`, `dict_get`, `dict_items`, `dict_keys`, `dict_pop`,/// `dict_popitem`, `dict_setdefault`, `dict_update`, `dict_values`, `list_append`, `list_clear`,/// `list_copy` and `list_pop`. Two bound methods are equal when their tags and objects match. Each/// read makes a new bound method, so two reads give equal values that are different objects. A/// bound method is hashable, so it can be a dictionary key.pub const NativeMethod = union(enum) {    dict_clear: *Dict,    dict_copy: *Dict,    dict_get: *Dict,    dict_items: *Dict,    dict_keys: *Dict,    dict_pop: *Dict,    dict_popitem: *Dict,    dict_setdefault: *Dict,    dict_update: *Dict,    dict_values: *Dict,    list_append: *List,    list_clear: *List,    list_copy: *List,    list_pop: *List,};/// A Python list, as a Zig growable array of values. The virtual machine's lists are these, and the/// package's property test and benchmark drive one directly. The array grows geometrically, so/// filling it takes few allocations. After `clearRetainingCapacity`, refilling the array to its old/// length allocates nothing and keeps the same storage. The virtual machine's `clear` method frees/// the storage. When `pop` or `del` leaves fewer than half of the slots in use, the virtual machine/// shrinks the storage to fit the items.pub const List = std.ArrayListUnmanaged(Value);/// A Python tuple, as a fixed slice of values. Tuple displays, `tuple()` and the pairs that/// dictionaries and `enumerate` yield are these. The virtual machine never changes a tuple after/// making it, so a tuple of hashable items can be a dictionary key.pub const Tuple = struct {    /// The tuple's values, in order. When the virtual machine made the tuple, the machine's heap    /// owns this array.    items: []Value,};/// A Python dictionary that keeps insertion order: an array of key and value entries, in the order/// the keys first arrived, and a hash table from each key to its entry's position. Dictionary/// displays, `dict()` and `copy()` make these, and the package's property test and benchmark drive/// one directly. An empty dictionary is `.{}`. Finding a key and replacing its value take expected/// constant time and allocate nothing. Removing an entry shifts every later entry down one position/// and updates each later entry's position in the hash table, so removal takes time linear in the/// number of later entries. The hash table grows when it is more than 80 percent full. Every key/// has to be hashable, and `put` asserts it.pub const Dict = struct {    /// The key and value pairs in insertion order. Iteration, `popLast` and equality read this    /// array.    entries: std.ArrayListUnmanaged(DictEntry) = .empty,    /// A hash table from each key to its entry's position in `entries`. The table hashes keys with    /// `Value.hash` and compares them with `Value.eql`. The table's type is private to this file,    /// so callers reach it through the methods below.    index: DictIndex = .empty,    /// Frees the entries and the hash table with the given allocator and leaves the dictionary    /// undefined. The heap frees each dictionary it owns with it. The call frees nothing that the    /// keys and values point to.    pub fn deinit(self: *Dict, allocator: std.mem.Allocator) void {        self.index.deinit(allocator);        self.entries.deinit(allocator);        self.* = undefined;    }    /// Removes every entry and keeps the memory of both the entries and the hash table for reuse.    /// The virtual machine's `dict.clear` method empties a dictionary with it. The call allocates    /// nothing, so it cannot fail.    pub fn clearRetainingCapacity(self: *Dict) void {        self.entries.clearRetainingCapacity();        self.index.clearRetainingCapacity();    }    /// Reserves room for at least `capacity` entries in both the entry array and the hash table.    /// `createDict`, dictionary displays and the benchmark reserve room with it before inserting    /// many keys. The call returns `error.OutOfMemory` when an allocation fails. The call also    /// returns `error.OutOfMemory` when `capacity` exceeds the largest 32-bit unsigned integer,    /// because the hash table counts its entries in 32 bits.    pub fn ensureTotalCapacity(self: *Dict, allocator: std.mem.Allocator, capacity: usize) std.mem.Allocator.Error!void {        if (capacity > std.math.maxInt(u32)) return error.OutOfMemory;        try self.entries.ensureTotalCapacity(allocator, capacity);        try self.index.ensureTotalCapacity(allocator, @intCast(capacity));    }    /// Returns the position of the key's entry in `entries`, or null when the key is absent. Every    /// dictionary lookup, membership test and deletion in the virtual machine starts with it. The    /// key has to be hashable, because `Value.hash` asserts it.    pub fn indexOf(self: *const Dict, key: Value) ?usize {        return self.index.get(key);    }    /// When an equal key is present, replaces that entry's value, and the entry keeps its position    /// and its first key. Any other key goes with its value into a new entry at the end. Every    /// dictionary insertion in the virtual machine goes through it. Replacing a value allocates    /// nothing. The call asserts that the key is hashable. On `error.OutOfMemory`, both the entry    /// array and the hash table stay as they were.    pub fn put(self: *Dict, allocator: std.mem.Allocator, key: Value, entry_value: Value) std.mem.Allocator.Error!void {        std.debug.assert(key.hashable());        if (self.index.get(key)) |entry_index| {            self.entries.items[entry_index].value = entry_value;            return;        }        const entry_index = self.entries.items.len;        try self.entries.append(allocator, .{ .key = key, .value = entry_value });        errdefer self.entries.items.len -= 1;        try self.index.put(allocator, key, entry_index);    }    /// Removes the entry at the given position and returns its value. `del d[k]` and `d.pop(k)`    /// remove an entry with it. The call shifts every later entry down one place and updates each    /// later entry's position in the hash table. The call takes time linear in the number of    /// entries after the position. The position has to be in range. The call allocates nothing, so    /// it cannot fail.    pub fn removeAt(self: *Dict, entry_index: usize) Value {        const removed = self.entries.orderedRemove(entry_index);        std.debug.assert(self.index.remove(removed.key));        for (self.entries.items[entry_index..], entry_index..) |entry, index| {            self.index.getPtr(entry.key).?.* = index;        }        return removed.value;    }    /// Removes the most recently inserted entry and returns it, or returns null when the dictionary    /// is empty. `d.popitem()` removes the newest entry with it. The call leaves every other entry    /// at its position.    pub fn popLast(self: *Dict) ?DictEntry {        const entry = self.entries.pop() orelse return null;        std.debug.assert(self.index.remove(entry.key));        return entry;    }};/// One key and its value in a dictionary. A dictionary's entry array holds these, and `createDict`/// takes a slice of them.pub const DictEntry = struct {    /// The entry's key, which is hashable.    key: Value,    /// The value stored under the key.    value: Value,};/// The part of a dictionary a view shows, one tag each, named after the Python method that returns/// the view: `keys`, `values` and `items`. A dictionary view carries one. An items view yields each/// entry as a two-item tuple of key and value.pub const DictViewKind = enum {    keys,    values,    items,};/// A live view of one dictionary. `keys()`, `values()` and `items()` return one. The view borrows/// the dictionary and shows every later change to it. A view's length and truth value are those of/// its dictionary. A keys view and an items view support membership and equality by content, and a/// values view equals only itself.pub const DictView = struct {    /// The dictionary that the view shows. The view borrows this dictionary.    dict: *Dict,    /// Which part of the dictionary the view shows.    kind: DictViewKind,};/// A Python range: the integers from `start` toward `stop` in steps of `step`, with the count of/// elements stored. `range()` and range slices make one. Membership is computed from `start`,/// `stop` and `step`, in constant time. Two ranges are equal when they yield the same elements,/// whatever their stops.pub const Range = struct {    /// The first element.    start: i128,    /// The bound the elements approach and never reach.    stop: i128,    /// The difference between neighboring elements. `range` and slicing reject a step of zero with    /// `ValueError`.    step: i128,    /// The number of elements, computed once by `range` or by a slice.    length: usize,};/// The state of one walk for `for` loops, `iter`, `next`, `enumerate`, `reversed`, and the/// consuming builtins, as one tag per kind of source: a dictionary's keys, `enumerate`, a/// dictionary view, a list, a tuple, a range, or a string. Each `next` call or loop step advances/// the walk, so items it passed are gone for every holder of the iterator. Each kind borrows what/// it walks, so a list or dictionary changed during the walk is read as it stands at each step. An/// iterator has no hash, so it cannot be a dictionary key. An iterator equals only itself.pub const Iterator = union(enum) {    /// Walks a dictionary's keys.    dict: DictIterator,    /// Pairs the items of another iterator with a counter.    enumerate: EnumerateIterator,    /// Walks a dictionary view.    view: DictViewIterator,    /// Walks a list.    list: ListIterator,    /// Walks a tuple.    tuple: TupleIterator,    /// Walks a range.    range: RangeIterator,    /// Walks a string by codepoint.    string: StringIterator,};/// The state of a walk over a list, from the first item or from the last, for `createListIterator`/// and `createListReverseIterator`. The walk ends when its count of items taken reaches the list's/// current length.pub const ListIterator = struct {    /// The borrowed list that the walk reads.    list: *List,    /// How many items the walk has taken, starting at 0. A walk from the end reads the item that    /// many places before the last.    index: usize = 0,    /// True for a walk from the last item to the first, and false by default.    reverse: bool = false,};/// The state of a walk over a dictionary's keys, in insertion order or from the newest key back,/// for `createDictIterator` and `createDictReverseIterator`. The walk checks nothing when the/// dictionary changes under it, so a key added or removed mid-walk shifts what the walk yields.pub const DictIterator = struct {    /// The borrowed dictionary that the walk reads.    dict: *Dict,    /// How many keys the walk has taken, starting at 0.    index: usize = 0,    /// True for a walk from the newest key to the oldest, and false by default.    reverse: bool = false,};/// The state of an `enumerate` walk for `createEnumerateIterator`: an inner iterator and the/// counter value its next item gets. Each step makes a new two-item tuple on the heap.pub const EnumerateIterator = struct {    /// The borrowed inner iterator, which each step advances by one item.    iterator: *Iterator,    /// The counter value for the next item. The counter starts at the `start` argument of    /// `enumerate`.    index: i128,    /// Set once the counter has reached the largest signed 128-bit integer. After that, one more    /// item from the inner iterator fails with `IntegerOverflow`, and an exhausted inner iterator    /// ends the walk.    overflowed: bool = false,};/// The state of a walk over a dictionary view, for `createDictViewIterator` and/// `createDictViewReverseIterator`: its keys, its values, or its entries as key and value tuples. A/// walk over an items view makes a new tuple on the heap for each entry.pub const DictViewIterator = struct {    /// The borrowed view that the walk reads.    view: *DictView,    /// How many entries the walk has taken, starting at 0.    index: usize = 0,    /// True for a walk from the newest entry to the oldest, and false by default.    reverse: bool = false,};/// The state of a walk over a tuple, from the first item or from the last, for/// `createTupleIterator` and `createTupleReverseIterator`.pub const TupleIterator = struct {    /// The borrowed tuple that the walk reads.    tuple: *Tuple,    /// How many items the walk has taken, starting at 0.    index: usize = 0,    /// True for a walk from the last item to the first, and false by default.    reverse: bool = false,};/// The state of a walk over a range's elements, from its start or from its last element, for/// `createRangeIterator` and `createRangeReverseIterator`. The walk fails with `IntegerOverflow`/// when the next element leaves the signed 128-bit range.pub const RangeIterator = struct {    /// The borrowed range that the walk reads.    range: *Range,    /// How many elements the walk has yielded, starting at 0. The walk ends when this count reaches    /// the range's length. A walk from the end computes each element from this count.    index: usize = 0,    /// The element that a forward walk yields next. A new iterator holds the range's start here. A    /// walk from the end leaves it unused.    next: i128,    /// True for a walk from the last element to the start, and false by default.    reverse: bool = false,};/// The state of a walk over a string's codepoints, from the first or from the last, for/// `createStringIterator` and `createStringReverseIterator`. Each step yields one codepoint as a/// string that points into the same bytes. The walk fails with `ValueError` when the bytes contain/// invalid UTF-8. Each step checks bytes for valid UTF-8 again: a forward walk checks the rest of/// the string, and a walk from the end checks all of it. A full walk therefore takes time quadratic/// in the string's length.pub const StringIterator = struct {    /// The borrowed bytes of the string that the walk reads.    value: []const u8,    /// The byte offset of the walk's position in the string. A forward walk starts this offset at 0    /// and moves it forward. A walk from the end starts this offset at the string's length and    /// moves it back.    index: usize = 0,    /// True for a walk from the last codepoint to the first, and false by default.    reverse: bool = false,};const ValueContext = struct {    pub fn hash(_: ValueContext, value: Value) u64 {        return value.hash();    }    pub fn eql(_: ValueContext, left: Value, right: Value) bool {        return left.eql(right);    }};const DictIndex = std.HashMapUnmanaged(Value, usize, ValueContext, 80);fn hashInto(hasher: *std.hash.Wyhash, value: Value) void {    if (value.integerLike()) |integer| {        hashTag(hasher, 1);        hasher.update(std.mem.asBytes(&integer));        return;    }    switch (value) {        .none => hashTag(hasher, 0),        .boolean, .integer => unreachable,        .string => |string| {            hashTag(hasher, 2);            hasher.update(std.mem.asBytes(&string.len));            hasher.update(string);        },        .function => |function| {            hashTag(hasher, 3);            hasher.update(std.mem.asBytes(&function));        },        .builtin => |builtin| {            hashTag(hasher, 4);            const tag: u8 = @backingInt(builtin);            hasher.update(&.{tag});        },        .method => |method| {            hashTag(hasher, 5);            hashNativeMethod(hasher, method);        },        .tuple => |tuple| {            hashTag(hasher, 6);            hasher.update(std.mem.asBytes(&tuple.items.len));            for (tuple.items) |item| hashInto(hasher, item);        },        .range => |range| {            hashTag(hasher, 7);            hasher.update(std.mem.asBytes(&range.length));            if (range.length != 0) hasher.update(std.mem.asBytes(&range.start));            if (range.length > 1) hasher.update(std.mem.asBytes(&range.step));        },        .list, .dict, .view, .iterator => unreachable,    }}fn hashTag(hasher: *std.hash.Wyhash, tag: u8) void {    hasher.update(&.{tag});}fn hashNativeMethod(hasher: *std.hash.Wyhash, method: *const NativeMethod) void {    const tag: u8 = @backingInt(std.meta.activeTag(method.*));    hasher.update(&.{tag});    const owner = switch (method.*) {        .dict_clear => |dict| @intFromPtr(dict),        .dict_copy => |dict| @intFromPtr(dict),        .dict_get => |dict| @intFromPtr(dict),        .dict_items => |dict| @intFromPtr(dict),        .dict_keys => |dict| @intFromPtr(dict),        .dict_pop => |dict| @intFromPtr(dict),        .dict_popitem => |dict| @intFromPtr(dict),        .dict_setdefault => |dict| @intFromPtr(dict),        .dict_update => |dict| @intFromPtr(dict),        .dict_values => |dict| @intFromPtr(dict),        .list_append => |list| @intFromPtr(list),        .list_clear => |list| @intFromPtr(list),        .list_copy => |list| @intFromPtr(list),        .list_pop => |list| @intFromPtr(list),    };    hasher.update(std.mem.asBytes(&owner));}fn listsEqual(left: *const List, right: *const List) bool {    if (left == right) return true;    return sequencesEqual(left.items, right.items);}fn sequencesEqual(left: []const Value, right: []const Value) bool {    if (left.len != right.len) return false;    for (left, right) |a, b| {        if (!a.eql(b)) return false;    }    return true;}fn sequenceHashable(values: []const Value) bool {    for (values) |item| {        if (!item.hashable()) return false;    }    return true;}fn nativeMethodsEqual(left: *const NativeMethod, right: *const NativeMethod) bool {    return switch (left.*) {        .dict_clear => |dict| right.* == .dict_clear and right.dict_clear == dict,        .dict_copy => |dict| right.* == .dict_copy and right.dict_copy == dict,        .dict_get => |dict| right.* == .dict_get and right.dict_get == dict,        .dict_items => |dict| right.* == .dict_items and right.dict_items == dict,        .dict_keys => |dict| right.* == .dict_keys and right.dict_keys == dict,        .dict_pop => |dict| right.* == .dict_pop and right.dict_pop == dict,        .dict_popitem => |dict| right.* == .dict_popitem and right.dict_popitem == dict,        .dict_setdefault => |dict| right.* == .dict_setdefault and right.dict_setdefault == dict,        .dict_update => |dict| right.* == .dict_update and right.dict_update == dict,        .dict_values => |dict| right.* == .dict_values and right.dict_values == dict,        .list_append => |list| right.* == .list_append and right.list_append == list,        .list_clear => |list| right.* == .list_clear and right.list_clear == list,        .list_copy => |list| right.* == .list_copy and right.list_copy == list,        .list_pop => |list| right.* == .list_pop and right.list_pop == list,    };}fn dictsEqual(left: *const Dict, right: *const Dict) bool {    if (left == right) return true;    if (left.entries.items.len != right.entries.items.len) return false;    for (left.entries.items) |entry| {        const index = right.indexOf(entry.key) orelse return false;        if (!entry.value.eql(right.entries.items[index].value)) return false;    }    return true;}fn dictViewsEqual(left: *const DictView, right: *const DictView) bool {    if (left == right) return true;    if (left.kind != right.kind) return false;    return switch (left.kind) {        .keys => dictKeysEqual(left.dict, right.dict),        .items => dictsEqual(left.dict, right.dict),        .values => false,    };}fn dictKeysEqual(left: *const Dict, right: *const Dict) bool {    if (left.entries.items.len != right.entries.items.len) return false;    for (left.entries.items) |entry| {        if (right.indexOf(entry.key) == null) return false;    }    return true;}fn rangesEqual(left: *const Range, right: *const Range) bool {    if (left.length != right.length) return false;    if (left.length == 0) return true;    if (left.start != right.start) return false;    if (left.length == 1) return true;    return left.step == right.step;}test "boolean values behave as Python integers in arithmetic contexts" {    try std.testing.expectEqual(@as(?i128, 1), (Value{ .boolean = true }).integerLike());    try std.testing.expectEqual(@as(?i128, 0), (Value{ .boolean = false }).integerLike());}test "equality follows Python integer boolean behavior" {    try std.testing.expect((Value{ .boolean = true }).eql(.{ .integer = 1 }));    try std.testing.expect((Value{ .boolean = false }).eql(.{ .integer = 0 }));    try std.testing.expect((Value{ .none = {} }).eql(.none));    try std.testing.expect(!(Value{ .none = {} }).eql(.{ .integer = 0 }));}test "string values have Python truthiness and equality" {    try std.testing.expect(!(Value{ .string = "" }).truthy());    try std.testing.expect((Value{ .string = "x" }).truthy());    try std.testing.expect((Value{ .string = "alpha" }).eql(.{ .string = "alpha" }));    try std.testing.expect(!(Value{ .string = "alpha" }).eql(.{ .string = "beta" }));}test "list values have Python truthiness and equality" {    var left_items = [_]Value{ .{ .integer = 1 }, .{ .string = "x" } };    var right_items = [_]Value{ .{ .integer = 1 }, .{ .string = "x" } };    var different_items = [_]Value{.{ .integer = 1 }};    var empty: List = .empty;    var left = List{ .items = &left_items, .capacity = left_items.len };    var right = List{ .items = &right_items, .capacity = right_items.len };    var different = List{ .items = &different_items, .capacity = different_items.len };    try std.testing.expect(!(Value{ .list = &empty }).truthy());    try std.testing.expect((Value{ .list = &left }).truthy());    try std.testing.expect((Value{ .list = &left }).eql(.{ .list = &right }));    try std.testing.expect(!(Value{ .list = &left }).eql(.{ .list = &different }));}test "tuple values have Python truthiness and equality" {    var left_items = [_]Value{ .{ .integer = 1 }, .{ .string = "x" } };    var right_items = [_]Value{ .{ .integer = 1 }, .{ .string = "x" } };    var different_items = [_]Value{.{ .integer = 1 }};    var empty = Tuple{ .items = &.{} };    var left = Tuple{ .items = &left_items };    var right = Tuple{ .items = &right_items };    var different = Tuple{ .items = &different_items };    try std.testing.expect(!(Value{ .tuple = &empty }).truthy());    try std.testing.expect((Value{ .tuple = &left }).truthy());    try std.testing.expect((Value{ .tuple = &left }).eql(.{ .tuple = &right }));    try std.testing.expect(!(Value{ .tuple = &left }).eql(.{ .tuple = &different }));}test "range values have Python truthiness and equality" {    var empty = Range{ .start = 0, .stop = 0, .step = 1, .length = 0 };    var single = Range{ .start = 1, .stop = 2, .step = 1, .length = 1 };    var same_single = Range{ .start = 1, .stop = 20, .step = 3, .length = 1 };    var left = Range{ .start = 0, .stop = 3, .step = 2, .length = 2 };    var right = Range{ .start = 0, .stop = 4, .step = 2, .length = 2 };    var different = Range{ .start = 0, .stop = 5, .step = 2, .length = 3 };    try std.testing.expect(!(Value{ .range = &empty }).truthy());    try std.testing.expect((Value{ .range = &single }).truthy());    try std.testing.expect((Value{ .range = &single }).eql(.{ .range = &same_single }));    try std.testing.expect((Value{ .range = &left }).eql(.{ .range = &right }));    try std.testing.expect(!(Value{ .range = &left }).eql(.{ .range = &different }));}test "iterator values are truthy and compare by identity" {    var list: List = .empty;    var left = Iterator{ .list = .{ .list = &list } };    var right = Iterator{ .list = .{ .list = &list } };    try std.testing.expect((Value{ .iterator = &left }).truthy());    try std.testing.expect((Value{ .iterator = &left }).eql(.{ .iterator = &left }));    try std.testing.expect(!(Value{ .iterator = &left }).eql(.{ .iterator = &right }));}test "equal hashable values share hashes" {    var left_tuple_items = [_]Value{ .{ .boolean = true }, .{ .string = "tuple" } };    var right_tuple_items = [_]Value{ .{ .integer = 1 }, .{ .string = "tuple" } };    var left_tuple = Tuple{ .items = &left_tuple_items };    var right_tuple = Tuple{ .items = &right_tuple_items };    var left_empty_range = Range{ .start = 4, .stop = 4, .step = 1, .length = 0 };    var right_empty_range = Range{ .start = 90, .stop = -10, .step = -5, .length = 0 };    var left_list: List = .empty;    var left_method = NativeMethod{ .list_append = &left_list };    var right_method = NativeMethod{ .list_append = &left_list };    const pairs = [_][2]Value{        .{ .{ .boolean = true }, .{ .integer = 1 } },        .{ .{ .string = "same" }, .{ .string = "same" } },        .{ .{ .tuple = &left_tuple }, .{ .tuple = &right_tuple } },        .{ .{ .range = &left_empty_range }, .{ .range = &right_empty_range } },        .{ .{ .method = &left_method }, .{ .method = &right_method } },    };    for (pairs) |pair| {        try std.testing.expect(pair[0].eql(pair[1]));        try std.testing.expectEqual(pair[0].hash(), pair[1].hash());    }}test "list geometric storage supports allocation-free retained refill" {    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});    var list: List = .empty;    defer list.deinit(failing.allocator());    for (0..4096) |index| try list.append(failing.allocator(), .{ .integer = @intCast(index) });    const capacity = list.capacity;    const pointer = list.items.ptr;    const growth_attempts = failing.alloc_index + failing.resize_index;    try std.testing.expect(capacity >= list.items.len);    try std.testing.expect(growth_attempts < 64);    list.clearRetainingCapacity();    failing.fail_index = failing.alloc_index;    failing.resize_fail_index = failing.resize_index;    for (0..4096) |index| try list.append(failing.allocator(), .{ .integer = @intCast(index) });    try std.testing.expect(!failing.has_induced_failure);    try std.testing.expectEqual(capacity, list.capacity);    try std.testing.expectEqual(pointer, list.items.ptr);}test "dictionary hashes equality while retaining insertion order" {    var dict: Dict = .{};    defer dict.deinit(std.testing.allocator);    try dict.put(std.testing.allocator, .{ .boolean = true }, .{ .string = "first" });    try dict.put(std.testing.allocator, .{ .integer = 2 }, .{ .string = "second" });    try dict.put(std.testing.allocator, .{ .integer = 1 }, .{ .string = "updated" });    try std.testing.expectEqual(@as(usize, 2), dict.entries.items.len);    try std.testing.expect(dict.entries.items[0].key == .boolean);    try std.testing.expectEqualStrings("updated", dict.entries.items[dict.indexOf(.{ .integer = 1 }).?].value.string);    _ = dict.removeAt(dict.indexOf(.{ .integer = 1 }).?);    try dict.put(std.testing.allocator, .{ .boolean = true }, .{ .string = "reinserted" });    try std.testing.expectEqual(@as(i128, 2), dict.entries.items[0].key.integer);    try std.testing.expect(dict.entries.items[1].key == .boolean);    try std.testing.expectEqual(@as(usize, 0), dict.indexOf(.{ .integer = 2 }).?);    try std.testing.expectEqual(@as(usize, 1), dict.indexOf(.{ .integer = 1 }).?);}test "dictionary insertion failure leaves both stores unchanged" {    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});    var dict: Dict = .{};    defer dict.deinit(failing.allocator());    try dict.entries.ensureTotalCapacity(failing.allocator(), 1);    failing.fail_index = failing.alloc_index;    try std.testing.expectError(error.OutOfMemory, dict.put(failing.allocator(), .{ .integer = 7 }, .none));    try std.testing.expectEqual(@as(usize, 0), dict.entries.items.len);    try std.testing.expectEqual(@as(u32, 0), dict.index.count());    failing.fail_index = std.math.maxInt(usize);    try dict.put(failing.allocator(), .{ .integer = 7 }, .none);    try std.testing.expectEqual(@as(usize, 0), dict.indexOf(.{ .integer = 7 }).?);}test "dictionary existing updates allocate no working memory" {    var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});    var dict: Dict = .{};    defer dict.deinit(failing.allocator());    for (0..1024) |index| try dict.put(failing.allocator(), .{ .integer = @intCast(index) }, .none);    failing.fail_index = failing.alloc_index;    failing.resize_fail_index = failing.resize_index;    for (0..1024) |index| {        const key = Value{ .integer = @intCast(index) };        try dict.put(failing.allocator(), key, key);        try std.testing.expectEqual(index, dict.indexOf(key).?);    }    try std.testing.expect(!failing.has_induced_failure);}

Audit

Definitions25
Public names41
Members66
Version26.7.0
Revisiondaab053ee433