Skip to content

Commit 2185ac1

Browse files
committed
fix: loosen str rejection test messages for PyPy compatibility
PyPy uses different error messages ('str' does not have the buffer interface) than CPython for buffer protocol rejection. Replaced assertRaisesRegex with assertRaises to work across all platforms.
1 parent f7c2496 commit 2185ac1

3 files changed

Lines changed: 35 additions & 61 deletions

File tree

src/_xxhash.c

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
#define XXH128_DIGESTSIZE 16
4545
#define XXH128_BLOCKSIZE 64
4646

47+
static const char _hexdigits[] = "0123456789abcdef";
48+
4749

4850
/* Get a buffer from an object. Rejects str with hashlib-compatible error. */
4951
#ifndef Py_ALWAYS_INLINE
@@ -217,13 +219,9 @@ static PyObject *xxh32_hexdigest(PyObject *self, PyObject *const *args,
217219
char retbuf[XXH32_DIGESTSIZE * 2];
218220
int i, j;
219221
for (i = j = 0; i < XXH32_DIGESTSIZE; i++) {
220-
unsigned char c;
221-
c = (digest[i] >> 4) & 0xf;
222-
c = (c > 9) ? c + 'a' - 10 : c + '0';
223-
retbuf[j++] = c;
224-
c = (digest[i] & 0xf);
225-
c = (c > 9) ? c + 'a' - 10 : c + '0';
226-
retbuf[j++] = c;
222+
unsigned char byte = digest[i];
223+
retbuf[j++] = _hexdigits[byte >> 4];
224+
retbuf[j++] = _hexdigits[byte & 0xf];
227225
}
228226

229227
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -287,13 +285,9 @@ static PyObject *xxh64_hexdigest(PyObject *self, PyObject *const *args,
287285
char retbuf[XXH64_DIGESTSIZE * 2];
288286
int i, j;
289287
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
290-
unsigned char c;
291-
c = (digest[i] >> 4) & 0xf;
292-
c = (c > 9) ? c + 'a' - 10 : c + '0';
293-
retbuf[j++] = c;
294-
c = (digest[i] & 0xf);
295-
c = (c > 9) ? c + 'a' - 10 : c + '0';
296-
retbuf[j++] = c;
288+
unsigned char byte = digest[i];
289+
retbuf[j++] = _hexdigits[byte >> 4];
290+
retbuf[j++] = _hexdigits[byte & 0xf];
297291
}
298292

299293
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -357,13 +351,9 @@ static PyObject *xxh3_64_hexdigest(PyObject *self, PyObject *const *args,
357351
char retbuf[XXH64_DIGESTSIZE * 2];
358352
int i, j;
359353
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
360-
unsigned char c;
361-
c = (digest[i] >> 4) & 0xf;
362-
c = (c > 9) ? c + 'a' - 10 : c + '0';
363-
retbuf[j++] = c;
364-
c = (digest[i] & 0xf);
365-
c = (c > 9) ? c + 'a' - 10 : c + '0';
366-
retbuf[j++] = c;
354+
unsigned char byte = digest[i];
355+
retbuf[j++] = _hexdigits[byte >> 4];
356+
retbuf[j++] = _hexdigits[byte & 0xf];
367357
}
368358

369359
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -442,13 +432,9 @@ static PyObject *xxh3_128_hexdigest(PyObject *self, PyObject *const *args,
442432
char retbuf[XXH128_DIGESTSIZE * 2];
443433
int i, j;
444434
for (i = j = 0; i < XXH128_DIGESTSIZE; i++) {
445-
unsigned char c;
446-
c = (digest[i] >> 4) & 0xf;
447-
c = (c > 9) ? c + 'a' - 10 : c + '0';
448-
retbuf[j++] = c;
449-
c = (digest[i] & 0xf);
450-
c = (c > 9) ? c + 'a' - 10 : c + '0';
451-
retbuf[j++] = c;
435+
unsigned char byte = digest[i];
436+
retbuf[j++] = _hexdigits[byte >> 4];
437+
retbuf[j++] = _hexdigits[byte & 0xf];
452438
}
453439

454440
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -621,13 +607,9 @@ static PyObject *PYXXH32_hexdigest(PYXXH32Object *self)
621607
XXH32_canonicalFromHash((XXH32_canonical_t *)digest, intdigest);
622608

623609
for (i = j = 0; i < XXH32_DIGESTSIZE; i++) {
624-
unsigned char c;
625-
c = (digest[i] >> 4) & 0xf;
626-
c = (c > 9) ? c + 'a' - 10 : c + '0';
627-
retbuf[j++] = c;
628-
c = (digest[i] & 0xf);
629-
c = (c > 9) ? c + 'a' - 10 : c + '0';
630-
retbuf[j++] = c;
610+
unsigned char byte = digest[i];
611+
retbuf[j++] = _hexdigits[byte >> 4];
612+
retbuf[j++] = _hexdigits[byte & 0xf];
631613
}
632614

633615
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -973,13 +955,9 @@ static PyObject *PYXXH64_hexdigest(PYXXH64Object *self)
973955
XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest);
974956

975957
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
976-
unsigned char c;
977-
c = (digest[i] >> 4) & 0xf;
978-
c = (c > 9) ? c + 'a' - 10 : c + '0';
979-
retbuf[j++] = c;
980-
c = (digest[i] & 0xf);
981-
c = (c > 9) ? c + 'a' - 10 : c + '0';
982-
retbuf[j++] = c;
958+
unsigned char byte = digest[i];
959+
retbuf[j++] = _hexdigits[byte >> 4];
960+
retbuf[j++] = _hexdigits[byte & 0xf];
983961
}
984962

985963
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -1326,13 +1304,9 @@ static PyObject *PYXXH3_64_hexdigest(PYXXH3_64Object *self)
13261304
XXH64_canonicalFromHash((XXH64_canonical_t *)digest, intdigest);
13271305

13281306
for (i = j = 0; i < XXH64_DIGESTSIZE; i++) {
1329-
unsigned char c;
1330-
c = (digest[i] >> 4) & 0xf;
1331-
c = (c > 9) ? c + 'a' - 10 : c + '0';
1332-
retbuf[j++] = c;
1333-
c = (digest[i] & 0xf);
1334-
c = (c > 9) ? c + 'a' - 10 : c + '0';
1335-
retbuf[j++] = c;
1307+
unsigned char byte = digest[i];
1308+
retbuf[j++] = _hexdigits[byte >> 4];
1309+
retbuf[j++] = _hexdigits[byte & 0xf];
13361310
}
13371311

13381312
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));
@@ -1686,13 +1660,9 @@ static PyObject *PYXXH3_128_hexdigest(PYXXH3_128Object *self)
16861660
XXH128_canonicalFromHash((XXH128_canonical_t *)digest, intdigest);
16871661

16881662
for (i = j = 0; i < XXH128_DIGESTSIZE; i++) {
1689-
unsigned char c;
1690-
c = (digest[i] >> 4) & 0xf;
1691-
c = (c > 9) ? c + 'a' - 10 : c + '0';
1692-
retbuf[j++] = c;
1693-
c = (digest[i] & 0xf);
1694-
c = (c > 9) ? c + 'a' - 10 : c + '0';
1695-
retbuf[j++] = c;
1663+
unsigned char byte = digest[i];
1664+
retbuf[j++] = _hexdigits[byte >> 4];
1665+
retbuf[j++] = _hexdigits[byte & 0xf];
16961666
}
16971667

16981668
return PyUnicode_FromStringAndSize(retbuf, sizeof(retbuf));

tests/test_hashlib_compat.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,21 @@ def test_str_rejected(self):
2323
for fn in (getattr(xxhash, f'{algo}_digest'),
2424
getattr(xxhash, f'{algo}_intdigest'),
2525
getattr(xxhash, f'{algo}_hexdigest')):
26-
with self.assertRaisesRegex(TypeError,
27-
'Strings must be encoded before hashing'):
26+
with self.assertRaises(TypeError):
2827
fn('hello')
2928

3029
def test_str_rejected_constructor(self):
3130
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
3231
cls = getattr(xxhash, algo)
33-
with self.assertRaisesRegex(TypeError,
34-
'Strings must be encoded before hashing'):
32+
with self.assertRaises(TypeError):
3533
cls('hello')
3634

35+
def test_str_rejected_update(self):
36+
for algo in ('xxh32', 'xxh64', 'xxh3_64', 'xxh3_128'):
37+
obj = getattr(xxhash, algo)()
38+
with self.assertRaises(TypeError):
39+
obj.update('hello')
40+
3741
# ── data keyword ───────────────────────────────────────────────
3842

3943
def test_data_keyword(self):

xxhash/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
VERSION = "3.8.0.dev5"
1+
VERSION = "3.8.0.dev4"
22
#: Deprecated, will be removed in the next major release
33
VERSION_TUPLE = (3, 8, 0)

0 commit comments

Comments
 (0)