◐ Shell
clean mode source ↗

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

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 ⚠️ Warning 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 ⚠️ Warning ❌ Merge conflicts detected (8 files):

⚔️ Lib/test/test_class.py (content)
⚔️ Lib/test/test_codecs.py (content)
⚔️ Lib/test/test_descr.py (content)
⚔️ Lib/test/test_hashlib.py (content)
⚔️ Lib/test/test_itertools.py (content)
⚔️ Lib/test/test_pickle.py (content)
⚔️ crates/stdlib/src/hashlib.rs (content)
⚔️ crates/vm/src/builtins/object.rs (content)

These conflicts must be resolved before merging into main.

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/None pattern 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 between PyHasher and PyHasherXof.

These three properties are identical to their PyHasher counterparts (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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.