Add SHA3 private attribute parity to hashlib hash objects by moreal · Pull Request #7134 · RustPython/RustPython
📝 Walkthrough
Walkthrough
This PR introduces Keccak-related helper functions and constants to the hashlib module, then exposes Keccak metadata properties (capacity bits, rate bits, suffix byte) on PyHasher and PyHasherXof classes.
Changes
| Cohort / File(s) | Summary |
|---|---|
Keccak Constants and Helpers crates/stdlib/src/hashlib.rs |
Added KECCAK_WIDTH_BITS constant and four private helper functions: keccak_suffix(), keccak_rate_bits(), keccak_capacity_bits(), and missing_hash_attribute() for Keccak metadata computation. |
PyHasher and PyHasherXof Enhancements crates/stdlib/src/hashlib.rs |
Added three new Python-facing computed properties to PyHasher and PyHasherXof: _capacity_bits(), _rate_bits(), and _suffix() to expose Keccak metadata with fallback error handling. |
Estimated code review effort
🎯 2 (Simple) | ⏱️ ~12 minutes
Possibly related PRs
- Make HASH/HASHXOF types immutable #7131: Modifies the same PyHasher and PyHasherXof types to change pyclass flags to IMMUTABLETYPE.
Suggested reviewers
- youknowone
Poem
🐰 A hasher's delight, so crisp and clean,
Keccak secrets now are seen,
Capacity, rate, and suffix too—
Python's metadata dreams come true! ✨
🚥 Pre-merge checks | ✅ 2 | ❌ 2
❌ Failed checks (2 warnings)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. | |
| Merge Conflict Detection | ❌ Merge conflicts detected (8 files): ⚔️ These conflicts must be resolved before merging into |
Resolve conflicts locally and push changes to this branch. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title accurately describes the main change: adding private attributes (_capacity_bits, _rate_bits, _suffix) to PyHasher and PyHasherXof classes for SHA3/Keccak parity. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing touches
- 📝 Generate docstrings
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
⚔️ Resolve merge conflicts (beta)
- Auto-commit resolved conflicts to branch
fix/fail-hashlib-test_extra_sha3 - Post resolved changes as copyable diffs in a comment
No actionable comments were generated in the recent review. 🎉
🧹 Recent nitpick comments
crates/stdlib/src/hashlib.rs (2)
157-181: Properties are correct; consider.ok_or_else()for conciseness.Minor style nit: the
match+Some/Nonepattern could be slightly more concise with.ok_or_else().fn _capacity_bits(&self, vm: &VirtualMachine) -> PyResult<usize> { let block_size = self.ctx.read().block_size(); keccak_capacity_bits(&self.name, block_size) .ok_or_else(|| vm.new_attribute_error(format!("'HASH' object has no attribute '_capacity_bits'"))) }This is purely stylistic — the current form is clear too.
256-280: Duplicated property bodies betweenPyHasherandPyHasherXof.These three properties are identical to their
PyHashercounterparts (lines 157–181), differing only in the"HASHXOF"vs"HASH"class name string. Given the#[pyclass]macro constraints, full dedup is awkward, but you could extract thin helpers that accept the class name:♻️ Example dedup
fn get_capacity_bits(name: &str, block_size: usize, class_name: &str, vm: &VirtualMachine) -> PyResult<usize> { keccak_capacity_bits(name, block_size) .ok_or_else(|| vm.new_attribute_error(format!("'{class_name}' object has no attribute '_capacity_bits'"))) } fn get_rate_bits(name: &str, block_size: usize, class_name: &str, vm: &VirtualMachine) -> PyResult<usize> { keccak_rate_bits(name, block_size) .ok_or_else(|| vm.new_attribute_error(format!("'{class_name}' object has no attribute '_rate_bits'"))) } fn get_suffix(name: &str, class_name: &str, vm: &VirtualMachine) -> PyResult<PyBytes> { keccak_suffix(name) .map(|s| vec![s].into()) .ok_or_else(|| vm.new_attribute_error(format!("'{class_name}' object has no attribute '_suffix'"))) }Then each
#[pygetset]becomes a one-liner delegating to the shared helper.As per coding guidelines, "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.