Skip to content

Commit 316f820

Browse files
committed
Fix thread safety issue in bisect_left usage (Fixes #421)
1 parent e6eafdc commit 316f820

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

qrcode/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,9 @@ def best_fit(self, start=None):
219219
data.write(buffer)
220220

221221
needed_bits = len(buffer)
222+
# Create a thread-local copy of the table to avoid concurrent access issues (Python 3.13+)
222223
self.version = bisect_left(
223-
util.BIT_LIMIT_TABLE[self.error_correction], needed_bits, start
224+
util.BIT_LIMIT_TABLE[self.error_correction][:], needed_bits, start
224225
)
225226
if self.version == 41:
226227
raise exceptions.DataOverflowError

qrcode/tests/test_thread_safe.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from unittest import mock
2+
import qrcode
3+
from qrcode import util
4+
5+
6+
def test_best_fit_passes_copy_to_bisect():
7+
"""
8+
Verify that best_fit passes a copy of BIT_LIMIT_TABLE to bisect_left
9+
to ensure thread safety in Python 3.13+.
10+
"""
11+
qr = qrcode.QRCode()
12+
qr.add_data("test data")
13+
14+
# Identify the global table being used
15+
original_table = util.BIT_LIMIT_TABLE[qr.error_correction]
16+
17+
with mock.patch("qrcode.main.bisect_left") as mock_bisect:
18+
# Mock return value to be a valid version number to avoid side effects
19+
mock_bisect.return_value = 1
20+
21+
qr.best_fit()
22+
23+
assert mock_bisect.called
24+
25+
# Check arguments: bisect_left(a, x, lo=0, hi=len(a), *, key=None)
26+
# We are interested in 'a' (the list)
27+
args, _ = mock_bisect.call_args
28+
passed_table = args[0]
29+
30+
# Verify content matches
31+
assert passed_table == original_table
32+
33+
# Verify it is a NEW object (copy), not the original global list
34+
# This confirms the thread-safety fix
35+
assert passed_table is not original_table

0 commit comments

Comments
 (0)