Skip to content

feat: expose public hash-unit function for diagnostic use - #12

Open
joalves wants to merge 1 commit into
mainfrom
feat/public-hash-unit-function
Open

feat: expose public hash-unit function for diagnostic use#12
joalves wants to merge 1 commit into
mainfrom
feat/public-hash-unit-function

Conversation

@joalves

@joalves joalves commented Jul 14, 2026

Copy link
Copy Markdown

Summary

  • Extracts the MD5 + base64url-no-padding unit hashing logic out of Context.get_unit_hash() into a standalone, module-level sdk.internal.hashing.hash_unit(unit: str) -> str function.
  • Context.get_unit_hash() now delegates to hash_unit() instead of duplicating the logic — single implementation, no behavior change.
  • Motivation: diagnostic/test tooling (e.g. cross-SDK test harness wrappers) needs to hash a unit value without constructing a fully-wired Context (Clock, ContextConfig, Future, ContextDataProvider, ContextPublisher, ContextEventLogger, VariableParser, AudienceMatcher). This mirrors equivalent changes already made in go-sdk (sdk.HashUnit) and dotnet-sdk (ABSmartly.Internal.Hashing.Md5.Hash).
  • Verified hash_unit("test") == "CY9rzUYh03PK3k6DJie09g", matching the canonical cross-SDK test vector (also covered by test/test_md5.py fixtures on another branch).

Test plan

  • Full existing test suite passes (pytest test/) — 92 passed, no regressions.
  • Manually verified hash_unit("test") produces the canonical hash CY9rzUYh03PK3k6DJie09g.

Summary by CodeRabbit

  • Refactor
    • Improved the consistency of unit hash generation across the SDK.
    • Existing unit hash results and public interfaces remain unchanged.

Extract the MD5 + base64url-no-padding unit hashing logic out of
Context.get_unit_hash() into a standalone sdk.internal.hashing.hash_unit()
function, so callers that only need the hash (e.g. diagnostic tooling)
don't have to construct a full Context. Context.get_unit_hash() now
delegates to it instead of duplicating the logic.
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds hash_unit to sdk.internal.hashing, computing an unpadded URL-safe Base64 encoding of an MD5 digest. Updates Context.get_unit_hash to use this helper and encode its result as ASCII bytes, removing the direct base64 and hashlib usage.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Poem

A rabbit hops through hashes bright,
MD5 moonbeams wrapped just right.
Base64 tails are tucked away,
Context finds its path today.
“One neat helper!” the bunny cries.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding a hash-unit helper and delegating Context hashing to it.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/public-hash-unit-function

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
sdk/internal/hashing.py (1)

5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add direct tests for hash_unit.

The canonical "test" vector is useful, but also cover Unicode input, padding removal, and parity with Context.get_unit_hash() so future changes cannot silently alter the assignment contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/internal/hashing.py` around lines 5 - 7, Add direct tests for hash_unit
covering the canonical "test" vector, Unicode input, removal of trailing Base64
padding, and equality with Context.get_unit_hash() for the same unit. Keep
assertions focused on preserving the existing assignment contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/internal/hashing.py`:
- Line 6: Update the hashlib.md5 call assigned to dig to pass
usedforsecurity=False, preserving the existing UTF-8 encoding and digest
behavior while explicitly marking the hash as compatibility-only.

---

Nitpick comments:
In `@sdk/internal/hashing.py`:
- Around line 5-7: Add direct tests for hash_unit covering the canonical "test"
vector, Unicode input, removal of trailing Base64 padding, and equality with
Context.get_unit_hash() for the same unit. Keep assertions focused on preserving
the existing assignment contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d2116e6b-0f3a-4ede-9b48-fa862ab95bf4

📥 Commits

Reviewing files that changed from the base of the PR and between 7910b9b and 180803d.

📒 Files selected for processing (2)
  • sdk/context.py
  • sdk/internal/hashing.py

Comment thread sdk/internal/hashing.py


def hash_unit(unit: str) -> str:
dig = hashlib.md5(unit.encode('utf-8')).digest()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the repository's declared Python support before using usedforsecurity.
rg -n \
  'requires-python|python_requires|target-version|python-version|\\.python-version|\\.tool-versions' \
  .

Repository: absmartly/python3-sdk

Length of output: 205


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- sdk/internal/hashing.py ---\n'
cat -n sdk/internal/hashing.py

printf '\n--- pyproject.toml ---\n'
cat -n pyproject.toml

Repository: absmartly/python3-sdk

Length of output: 250


🌐 Web query:

Python hashlib md5 usedforsecurity introduced version 3.9 docs

💡 Result:

The keyword-only argument usedforsecurity was introduced to the hashlib module in Python 3.9 [1][2]. All hashlib constructors (including md5) were updated in version 3.9 to include this parameter, which defaults to True [1][3]. The purpose of this parameter is to allow the use of hashing algorithms that might otherwise be blocked or considered insecure in restricted environments (such as those enforcing FIPS compliance) [1][2]. By setting usedforsecurity to False, a developer indicates that the hashing algorithm is not being used in a security-sensitive context—for example, when using a hash function strictly as a non-cryptographic, one-way compression function [1][4].

Citations:


Mark this compatibility hash as non-security use. Add usedforsecurity=False so the intent is explicit and FIPS-mode environments do not reject it. The project already requires Python >=3.9, so the keyword is available.

🧰 Tools
🪛 Ruff (0.15.21)

[error] 6-6: Probable use of insecure hash functions in hashlib: md5

(S324)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@sdk/internal/hashing.py` at line 6, Update the hashlib.md5 call assigned to
dig to pass usedforsecurity=False, preserving the existing UTF-8 encoding and
digest behavior while explicitly marking the hash as compatibility-only.

Source: Linters/SAST tools

joalves added a commit to absmartly/cross-sdk-tests that referenced this pull request Jul 14, 2026
Replace the wrapper's own inline MD5 + base64url computation with a call
to python3-sdk's new sdk.internal.hashing.hash_unit(), matching the
go-sdk/dotnet-sdk/kotlin-wrapper pattern of routing this diagnostic
through the real SDK's hash function instead of a side-channel copy.

Verified locally (Dockerfile builds python3-sdk from the local
/Users/joalves/git_tree/sdks/python3-sdk checkout): 202/202 scenarios
pass and hashUnit(value="test") returns the canonical
"CY9rzUYh03PK3k6DJie09g".

Depends on absmartly/python3-sdk#12 (adds hash_unit) being merged
upstream before this works against a non-local/CI build of python3-sdk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant