Skip to content

Commit

Permalink
Support free-threaded Python 3.13 (#925)
Browse files Browse the repository at this point in the history
* Support free-threaded Python 3.13

* fix issues with tox.ini

* add multithreading test

* fix pep8

* fix ruff format

* name linux CI run based on python VERSION

* clarify use of pytest-run-parallel marks

* refactor test_multithreading

* simplify test_multithreading

* remove pytest-run-parallel use
  • Loading branch information
ngoldbaum authored Jan 15, 2025
1 parent 1de1cad commit df983f9
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 8 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ jobs:
PYTHON:
- {VERSION: "3.8", TOXENV: "py38"}
- {VERSION: "3.13", TOXENV: "py313"}
- {VERSION: "3.13t", TOXENV: "py313"}
MACOS:
- macos-13
- macos-latest
Expand All @@ -24,7 +25,7 @@ jobs:
- uses: actions/checkout@v4.2.2
- name: Setup python
id: setup-python
uses: actions/setup-python@v5.3.0
uses: quansight-labs/setup-python@v5.3.1
with:
python-version: ${{ matrix.PYTHON.VERSION }}
- uses: actions/cache@v4.2.0
Expand Down Expand Up @@ -53,12 +54,13 @@ jobs:
PYTHON:
- {VERSION: "3.8", TOXENV: "py38"}
- {VERSION: "3.13", TOXENV: "py313"}
- {VERSION: "3.13t", TOXENV: "py313"}
name: "Python ${{ matrix.PYTHON.VERSION }} on ${{ matrix.WINDOWS.WINDOWS }}"
steps:
- uses: actions/checkout@v4.2.2
- name: Setup python
id: setup-python
uses: actions/setup-python@v5.3.0
uses: quansight-labs/setup-python@v5.3.1
with:
python-version: ${{ matrix.PYTHON.VERSION }}
architecture: ${{ matrix.WINDOWS.ARCH }}
Expand Down Expand Up @@ -92,19 +94,20 @@ jobs:
- {VERSION: "3.11", TOXENV: "py311"}
- {VERSION: "3.12", TOXENV: "py312"}
- {VERSION: "3.13", TOXENV: "py313"}
- {VERSION: "3.13t", TOXENV: "py313"}
- {VERSION: "pypy-3.9", TOXENV: "pypy3"}
- {VERSION: "pypy-3.10", TOXENV: "pypy3"}

# MSRV
- {VERSION: "3.13", TOXENV: "py313", RUST_VERSION: "1.64.0"}
- {VERSION: "3.13", TOXENV: "py313", RUST_VERSION: "beta"}
- {VERSION: "3.13", TOXENV: "py313", RUST_VERSION: "nightly"}
name: "${{ matrix.PYTHON.TOXENV }} on linux, Rust ${{ matrix.PYTHON.RUST_VERSION || 'stable' }}"
name: "${{ matrix.PYTHON.VERSION }} on linux, Rust ${{ matrix.PYTHON.RUST_VERSION || 'stable' }}"
steps:
- uses: actions/checkout@v4.2.2
- name: Setup python
id: setup-python
uses: actions/setup-python@v5.3.0
uses: quansight-labs/setup-python@v5.3.1
with:
python-version: ${{ matrix.PYTHON.VERSION }}
- uses: actions/cache@v4.2.0
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ Compatibility
-------------

This library should be compatible with py-bcrypt and it will run on Python
3.6+, and PyPy 3.
3.8+ (including free-threaded builds), and PyPy 3.

Security
--------
Expand Down
2 changes: 1 addition & 1 deletion src/_bcrypt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ fn kdf<'p>(
})
}

#[pyo3::pymodule]
#[pyo3::pymodule(gil_used = false)]
mod _bcrypt {
use pyo3::types::PyModuleMethods;

Expand Down
28 changes: 26 additions & 2 deletions tests/test_bcrypt.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import uuid
from concurrent.futures import ThreadPoolExecutor

import pytest

import bcrypt
Expand Down Expand Up @@ -171,7 +174,7 @@
]


def test_gensalt_basic(monkeypatch):
def test_gensalt_basic():
salt = bcrypt.gensalt()
assert salt.startswith(b"$2b$12$")

Expand Down Expand Up @@ -219,7 +222,7 @@ def test_gensalt_bad_prefix():
bcrypt.gensalt(prefix=b"bad")


def test_gensalt_2a_prefix(monkeypatch):
def test_gensalt_2a_prefix():
salt = bcrypt.gensalt(prefix=b"2a")
assert salt.startswith(b"$2a$12$")

Expand Down Expand Up @@ -494,3 +497,24 @@ def test_2a_wraparound_bug():
)
== b"$2a$04$R1lJ2gkNaoPGdafE.H.16.1MKHPvmKwryeulRe225LKProWYwt9Oi"
)


def test_multithreading():
def create_user(pw):
salt = bcrypt.gensalt(4)
hash_ = bcrypt.hashpw(pw, salt)
key = bcrypt.kdf(pw, salt, 32, 50)
assert bcrypt.checkpw(pw, hash_)
return (salt, hash_, key)

user_creator = ThreadPoolExecutor(max_workers=4)
pws = [uuid.uuid4().bytes for _ in range(50)]

futures = [user_creator.submit(create_user, pw) for pw in pws]

users = [future.result() for future in futures]

for pw, (salt, hash_, key) in zip(pws, users):
assert bcrypt.hashpw(pw, salt) == hash_
assert bcrypt.checkpw(pw, hash_)
assert bcrypt.kdf(pw, salt, 32, 50) == key

0 comments on commit df983f9

Please sign in to comment.