Add `set` object functions to c-api by bschoenmaeckers · Pull Request #8002 · RustPython/RustPython
🌐 Web query:
CPython C API documentation for PySet_Discard: what are its return-value semantics, and is it defined as a single discard operation rather than a separate contains check followed by discard?
💡 Result:
PySet_Discard returns 1 if the element was found and removed, 0 if the element was not found (no action taken), and -1 if an error is encountered [1][2]. It does not raise a KeyError if the element is missing [1][2]. Regarding your second question, PySet_Discard is implemented as a single, atomic-like lookup-and-discard operation rather than separate "contains" and "discard" checks [3][4][5]. The underlying C implementation uses a function (typically named set_discard_entry in the CPython source) that performs a hash table lookup using the provided key [3][5]. If the key is found, it performs the removal in the same pass by updating the entry (replacing the key with a dummy tombstone, updating the set's size, and decrementing the reference count) [3][4][5]. This single-pass approach is more efficient and avoids the redundancy of performing two distinct lookups [5].
Citations:
- 1: https://docs.python.org/3/c-api/set.html
- 2: https://github.com/python/cpython/blob/master/Doc/c-api/set.rst
- 3: https://github.com/python/cpython/blob/main/Objects/setobject.c
- 4: https://github.com/python/cpython/blob/fd76eb547dd5d2c8307a89422049b6c3c80541ab/Objects/setobject.c
- 5: https://gopy.tamnd.com/docs/annotations/objects/setobject_c_detail
🏁 Script executed:
#!/bin/bash set -euo pipefail # 1) Show the exact current implementation around the referenced lines sed -n '60,120p' crates/capi/src/setobject.rs | nl -ba # 2) Find the definition and any related helpers rg -n "PySet_Discard|__contains__|fn discard|discard\(" crates/capi/src/setobject.rs crates/capi/src -S || true # 3) Search for a bool-returning discard helper in the broader crate rg -n "discard.*->.*bool|-> *Result<.*bool|discard.*bool|contains.*discard" crates/capi/src -S || true # 4) Run clippy as requested by guidelines (may be slow; best-effort) cargo clippy -q || true
Repository: RustPython/RustPython
Length of output: 108
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== setobject.rs around PySet_Discard ==" awk 'NR>=60 && NR<=120 {printf "%5d %s\n", NR, $0}' crates/capi/src/setobject.rs echo echo "== occurrences in crates/capi/src ==" rg -n "PySet_Discard|__contains__|\\bPySetInner\\b|\\bPySet\\b|fn +discard\\b|discard\\(" crates/capi/src -S echo echo "== look for discard helpers returning bool (best-effort) ==" rg -n "discard.*->.*bool|Result<.*bool>|fn +discard\\b" crates/capi/src -S echo echo "== best-effort cargo clippy (per guidelines) ==" cargo clippy -q || echo "cargo clippy failed (toolchain/build error in sandbox)"
Repository: RustPython/RustPython
Length of output: 4212
🏁 Script executed:
#!/bin/bash set -euo pipefail # Find PySet impl block / method definitions rg -n "impl +PySet|struct +PySet|pub +fn +discard\\b|fn +discard\\b|__contains__\\b" crates -S | head -n 200 echo "----" # Narrow to files likely defining PySet methods: search for "__contains__" method on PySet rg -n "PySet.*__contains__|__contains__\\(.*vm\\)" crates -S | head -n 200 echo "----" # Search for discard method returning Result/Option/boolean rg -n "PySet.*discard\\b|fn +discard\\b.*->|discard\\b.*->" crates -S | head -n 200 echo "----" echo "== best-effort cargo clippy ==" cargo clippy -q
Repository: RustPython/RustPython
Length of output: 11361
🏁 Script executed:
#!/bin/bash set -euo pipefail # Find PySet impl block / method definitions rg -n "impl +PySet|struct +PySet|pub +fn +discard\\b|fn +discard\\b|__contains__\\b" crates -S | head -n 200 echo "----" # Narrow to files likely defining PySet methods: search for "__contains__" method on PySet rg -n "PySet.*__contains__|__contains__\\(.*vm\\)" crates -S | head -n 200 echo "----" # Search for discard method returning Result/Option/boolean rg -n "PySet.*discard\\b|fn +discard\\b.*->|discard\\b.*->" crates -S | head -n 200 echo "----" echo "== best-effort cargo clippy ==" cargo clippy -q
Repository: RustPython/RustPython
Length of output: 11633
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== locate PySet methods / impls ==" rg -n "impl +PySet|struct +PySet|__contains__\\b|\\bdiscard\\b" crates -S | head -n 200 echo echo "== locate __contains__ specifically ==" rg -n "PySet.*__contains__|__contains__\\(.*vm\\)" crates -S | head -n 200 echo echo "== locate discard signatures ==" rg -n "PySet.*discard\\b|fn +discard\\b.*->|\\bdiscard\\b.*->" crates -S | head -n 200 echo echo "== cargo clippy (best-effort) ==" cargo clippy -q || echo "cargo clippy failed (toolchain/build error in sandbox)"
Repository: RustPython/RustPython
Length of output: 13841
🏁 Script executed:
#!/bin/bash set -euo pipefail # Print relevant sections of PySet implementation in RustPython echo "== crates/vm/src/builtins/set.rs: PySetInner::discard (around 320-360) ==" sed -n '300,380p' crates/vm/src/builtins/set.rs | nl -ba || true echo echo "== crates/vm/src/builtins/set.rs: PySet::discard (around 640-710) ==" sed -n '640,720p' crates/vm/src/builtins/set.rs | nl -ba || true echo echo "== crates/vm/src/builtins/set.rs: PySet::__contains__ (search + print nearby) ==" rg -n "pub fn __contains__\\b|fn __contains__\\b" crates/vm/src/builtins/set.rs # Print a small window around each match (best-effort) for ln in $(rg -n "pub fn __contains__\\b|fn __contains__\\b" crates/vm/src/builtins/set.rs | cut -d: -f1 | head -n 20); do echo echo "--- around line $ln ---" sed -n "$((ln-30)),$((ln+30))p" crates/vm/src/builtins/set.rs | head -n 120 done echo echo "== search for any public discard returning bool ==" rg -n "pub +fn +discard\\b.*bool|pub\\(crate\\)? +fn +discard\\b.*bool" crates/vm/src/builtins/set.rs
Repository: RustPython/RustPython
Length of output: 3946
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE="crates/vm/src/builtins/set.rs" echo "== PySetInner::discard around ~320-370 ==" sed -n '300,380p' "$FILE" echo echo "== PySet::discard around ~650-710 ==" sed -n '650,720p' "$FILE" echo echo "== PySetInner::contains around wherever it is (search) ==" rg -n "impl PySetInner|fn contains\\b|PySetInner.*contains\\b" "$FILE" | head -n 80 echo echo "== show body around first PySetInner::contains match (best-effort) ==" ln="$(rg -n "fn contains\\b" "$FILE" | head -n 1 | cut -d: -f1)" if [ -n "$ln" ]; then start=$((ln-40)) end=$((ln+60)) sed -n "${start},${end}p" "$FILE" fi
Repository: RustPython/RustPython
Length of output: 7814
Avoid the extra membership probe in PySet_Discard.
Current PySet_Discard does __contains__ and then (conditionally) calls discard, repeating the set lookup and any __hash__/__eq__ side effects; CPython’s PySet_Discard performs lookup+removal in one operation and returns 1/0 accordingly. Add a bool-returning discard helper on PySet (wrapping the existing PySetInner::discard(...)->PyResult<bool>) and call it once here.
pub unsafe extern "C" fn PySet_Discard(set: *mut PyObject, key: *mut PyObject) -> c_int { with_vm(|vm| { let set = unsafe { &*set }.try_downcast_ref::<PySet>(vm)?; let key = unsafe { &*key }; let had_item = set.__contains__(key, vm)?; if had_item { set.discard(key.to_owned(), vm)?; } Ok(had_item)