Skip to content

feat: add python bindings - #92

Open
alexleach wants to merge 8 commits into
shssoichiro:masterfrom
alexleach:master
Open

feat: add python bindings#92
alexleach wants to merge 8 commits into
shssoichiro:masterfrom
alexleach:master

Conversation

@alexleach

Copy link
Copy Markdown

Hi,

Would you be willing to add Python bindings to zxcvbn-rs? I have a separate Issue that arises as the existing zxcvbn-python library outputs integers larger than 64-bits, which another rust-python library orjson then breaks on.

By adding Python bindings, I have found the zxcvbn function outputs only 64-bit integers, maximum, fixing the incompatibility issue we have between Python's zxcvbn and orjson packages.

Honest disclosure: Codex (GPT-5.3) added these Python bindings and I have otherwise done minimal review. It has added some unit tests, which pass, and it works for my needs, too.

Summary

A single function is exposed to Python and can be used as a drop-in replacement for the zxcvbn-python package's function.

Usage

import zxcvbn_rs
rs_result = zxcvbn_rs.zxcvbn('fVpIFpLiMsHncvqrwF6BgnQXeRhCY/et9x1s4cckhPaP0XJze9CWPkEF+19dA3ktgYmLK59QDWttfng1/TtkP0fciEcdwsARXEFkSbqAXVm/83hXJGAIiR1l14Feu171')
print("guesses: {0}".format(rs_result['guesses']))
print("Bit length: {0}".format(rs_result['guesses'].bit_length())

Output

guesses: 18446744073709551615
Bit length: 64

The only difference I can see is the rust version does not have a max_length argument, which the Python version allows. Instead, it Only evaluate the first 100 characters of the input. This prevents potential DoS attacks from sending extremely long input strings., which I think is fine.

Would you be happy to take this on? 🙏

Benchmarks

>>> pw = 'fVpIFpLiMsHncvqrwF6BgnQXeRhCY/et9x1s4cckhPaP0XJze9CWPkEF+19dA3ktgYmLK59QDWttfng1/TtkP0fciEcdwsARXEFkSbqAXVm/83hXJGAIiR1l14Feu171'
>>> timeit.timeit('zxcvbn_rs.zxcvbn(pw)', setup='import zxcvbn_rs', globals={'pw':pw}, number=1000 )
5.171430707996478
>>> timeit.timeit('zxcvbn_py.zxcvbn(pw, max_length=128)', setup='import zxcvbn as zxcvbn_py', globals={'pw':pw}, number=1000 )
92.34161645799759

Shows a 17x speedup for this 128-bit long random base64 string.

Cheers,
Alex

@alexleach

Copy link
Copy Markdown
Author

Ah, just seen someone has already done this and is maintaining it in a separate repository! It looks like they have more surface coverage, too.

https://pypi.org/project/zxcvbn-rs-py/

@mergify

mergify Bot commented Jun 28, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

@alexleach

Copy link
Copy Markdown
Author

Hi, re-opening this PR. I tried using https://github.com/fief-dev/zxcvbn-rs-py, but it didn't work for our use case, as its output can't be serialised or iterated over.

So, I created a PR there (fief-dev/zxcvbn-rs-py#9) back in March, but I've had no response, so thought I'd try this route again.

This PR would help create a PyPi package called zxcvbn-rs, which does not exist currently. There are already Python packages for http://pypi.org/project/zxcvbn and http://pypi.org/project/zxcvbn-rs-py. I would prefer if a third PyPi package did not exist, but to resolve frappe/frappe#37676, we need a Rust implementation which can produce serialised output.

Would you be interested in supporting Python bindings to this package, please?

Comment thread bindings/python/src/lib.rs
Comment thread bindings/python/src/lib.rs Outdated
Comment thread bindings/python/src/lib.rs
Comment thread bindings/python/src/lib.rs
@kilo-code-bot

kilo-code-bot Bot commented Jun 28, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
bindings/python/src/lib.rs 37 feedback is always serialized as a dict, diverging from zxcvbn-python which returns None for strong passwords

SUGGESTION

File Line Issue
bindings/python/src/lib.rs 60 Unrecognized dictionary_name values fall back to PascalCase
bindings/python/src/lib.rs 93 max_length counts Unicode scalar points, not grapheme clusters or bytes
Files Reviewed (3 files)
  • .github/workflows/zxcvbn.yml - 0 issues
  • bindings/python/src/lib.rs - 3 issues
  • bindings/python/tests/test_zxcvbn_rs.py - 0 issues

Fix these issues in Kilo Cloud

Previous Review Summaries (2 snapshots, latest commit ce5c122)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit ce5c122)

Status: 7 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 1
WARNING 4
SUGGESTION 2
Issue Details (click to expand)

CRITICAL

File Line Issue
bindings/python/src/lib.rs 105 py.allow_threads holds a borrowed &str into Python memory while the GIL is released

WARNING

File Line Issue
bindings/python/src/lib.rs 37 Feedback is always serialized as a dict, but zxcvbn-python returns None for strong passwords
bindings/python/src/lib.rs 80 Sequence guesses_log10 is recomputed from the saturated integer guess count
.github/workflows/zxcvbn.yml 16 Python 3.8 is end-of-life and may fail on ARM macOS runners
bindings/python/tests/test_zxcvbn_rs.py 89 GIL-release test is timing-dependent and may flake

SUGGESTION

File Line Issue
bindings/python/src/lib.rs 60 Unrecognized dictionary_name values fall back to PascalCase
bindings/python/src/lib.rs 96 max_length counts Unicode scalar points, not grapheme clusters or bytes
Files Reviewed (9 files)
  • .github/workflows/zxcvbn.yml - 1 issue
  • README.md - 0 issues
  • bindings/python/.gitignore - 0 issues
  • bindings/python/Cargo.toml - 0 issues
  • bindings/python/README.md - 0 issues
  • bindings/python/pyproject.toml - 0 issues
  • bindings/python/python/zxcvbn_rs/__init__.py - 0 issues
  • bindings/python/src/lib.rs - 4 issues
  • bindings/python/tests/test_zxcvbn_rs.py - 1 issue

Fix these issues in Kilo Cloud

Previous review (commit cf4c157)

Status: 4 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 3
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
bindings/python/src/lib.rs 34 Feedback is always serialized as a dict, but zxcvbn-python returns None for strong passwords
bindings/python/src/lib.rs 56 zxcvbn computation holds the Python GIL
bindings/python/src/lib.rs 80 Result omits the input password field

SUGGESTION

File Line Issue
bindings/python/src/lib.rs 84 Sequence dictionary_name values use PascalCase
Files Reviewed (8 files)
  • README.md - 0 issues
  • bindings/python/.gitignore - 0 issues
  • bindings/python/Cargo.toml - 0 issues
  • bindings/python/README.md - 0 issues
  • bindings/python/pyproject.toml - 0 issues
  • bindings/python/python/zxcvbn_rs/__init__.py - 0 issues
  • bindings/python/src/lib.rs - 4 issues
  • bindings/python/tests/test_zxcvbn_rs.py - 0 issues

Fix these issues in Kilo Cloud


Reviewed by kimi-k2.7-code-20260612 · Input: 51K · Output: 13.6K · Cached: 298.8K

@alexleach

Copy link
Copy Markdown
Author

I had Codex (GPT-5.5) review the PR and @kilo-code-bot's comments. It has produced a series of commits and this response:-

• I’ve pushed four follow-up commits addressing the review feedback:

  • Improved compatibility with zxcvbn-python, including output fields and dictionary names.
  • Released the GIL during password analysis.
  • Added strict JSON serialization, max_length, saturation, user-input, and concurrency tests.
  • Added Python 3.8/3.13 CI across Linux, macOS, and Windows.
  • Documented intentional API differences and versioning.

The Python ABI3 wheel builds successfully and all nine binding tests pass.

One clarification on the feedback thread: current zxcvbn returns a feedback dictionary for strong passwords with an empty warning string, rather than None; the binding now follows that behaviour.

Comment thread bindings/python/src/lib.rs Outdated
Comment thread bindings/python/src/lib.rs Outdated
Comment thread .github/workflows/zxcvbn.yml Outdated
Comment thread bindings/python/tests/test_zxcvbn_rs.py Outdated
Comment thread bindings/python/src/lib.rs
Comment thread bindings/python/src/lib.rs
@alexleach

Copy link
Copy Markdown
Author

Next iteration done with Codex. It's comment:-

I’ve pushed two additional cleanup commits addressing the latest review:

  • The password is now owned before releasing the GIL.
  • Misleading per-match guesses_log10 values derived from saturated integers were removed.
  • The timing-dependent GIL test was removed to avoid CI flakes.
  • Python 3.8 remains supported and tested on Linux, while Python 3.13 is tested across Linux, macOS, and Windows.

The remaining suggestions require no code changes: all current dictionary variants are explicitly mapped, and max_length uses Python-compatible Unicode character counting, as documented.

The update removes 25 net lines. The ABI3 wheel builds successfully, cargo check passes, and all eight tests pass.

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