diff -pruN 3.7.1-1/.github/workflows/build_wheels.yml 3.7.2-1/.github/workflows/build_wheels.yml
--- 3.7.1-1/.github/workflows/build_wheels.yml	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/.github/workflows/build_wheels.yml	2025-10-08 13:23:16.000000000 +0000
@@ -8,25 +8,26 @@ jobs:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        # macos-13 is an intel runner, macos-14 is apple silicon
-        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-14]
+        # macos-15-intel is an Intel runner, macos-14 is Apple silicon
+        os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-14]
 
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v5
 
       - name: Set up QEMU
         if: runner.os == 'Linux' && runner.arch == 'X64'
-        uses: docker/setup-qemu-action@v3.2.0
+        uses: docker/setup-qemu-action@v3
         with:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel@v2.22.0
+        uses: pypa/cibuildwheel@v3.2.0
         # to supply options, put them in 'env', like:
         env:
           # configure cibuildwheel to build native archs ('auto'), and some
           # emulated ones
           CIBW_ARCHS_LINUX: ${{ runner.arch == 'X64' && 'auto ppc64le s390x' || 'auto' }}
+          CIBW_SKIP: cp314t-*
           CIBW_TEST_COMMAND: python -c "import bitarray; assert bitarray.test().wasSuccessful()"
 
       - uses: actions/upload-artifact@v4
diff -pruN 3.7.1-1/CHANGE_LOG 3.7.2-1/CHANGE_LOG
--- 3.7.1-1/CHANGE_LOG	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/CHANGE_LOG	2025-10-08 13:23:16.000000000 +0000
@@ -1,3 +1,11 @@
+2025-10-08   3.7.2:
+-------------------
+  * enable `util.random_k()` for all supported Python versions,
+    previously this functions required Python 3.9 or higher
+  * add official Python 3.14 support
+  * update cibuildwheel to 3.2.0
+
+
 2025-08-28   3.7.1:
 -------------------
   * fix type hinting for memoryviews, see #241
diff -pruN 3.7.1-1/README.rst 3.7.2-1/README.rst
--- 3.7.1-1/README.rst	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/README.rst	2025-10-08 13:23:16.000000000 +0000
@@ -44,7 +44,7 @@ Key features
 Installation
 ------------
 
-Python wheels are are available on PyPI for all mayor platforms and Python
+Python wheels are are available on PyPI for all major platforms and Python
 versions.  Which means you can simply:
 
 .. code-block:: shell-session
@@ -57,7 +57,7 @@ Once you have installed the package, you
 
     $ python -c 'import bitarray; bitarray.test()'
     bitarray is installed in: /Users/ilan/bitarray/bitarray
-    bitarray version: 3.7.1
+    bitarray version: 3.7.2
     sys.version: 3.13.5 (main, Jun 16 2025) [Clang 18.1.8]
     sys.prefix: /Users/ilan/miniforge
     pointer size: 64 bit
@@ -319,7 +319,7 @@ and can therefore be used as a dictionar
 Reference
 =========
 
-bitarray version: 3.7.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
+bitarray version: 3.7.2 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
 
 In the following, ``item`` and ``value`` are usually a single bit -
 an integer 0 or 1.
@@ -697,6 +697,11 @@ This sub-module was added in version 1.2
    Also, ``a`` may be any object that exposes a writable buffer.
    Nothing about this function is specific to bitarray objects.
 
+   We should mention that Python's ``array.array`` object has a
+   method ``.byteswap()`` with similar functionality.  However, unlike
+   bitarray's ``util.byteswap()`` function, this method is limited to
+   swapping 2, 4, or 8 consecutive bytes.
+
    New in version 3.4
 
 
@@ -838,11 +843,7 @@ This sub-module was added in version 1.2
    set to one.  Mathematically equivalent to setting (in a bitarray of
    length ``n``) all bits at indices ``random.sample(range(n), k)`` to one.
    The random bitarrays are reproducible when giving Python's ``random.seed()``
-   with a specific seed value.
-
-   This function requires Python 3.9 or higher, as it depends on the standard
-   library function ``random.randbytes()``.  Raises ``NotImplementedError``
-   when Python version is too low.
+   a specific seed value.
 
    New in version 3.6
 
diff -pruN 3.7.1-1/bitarray/__init__.py 3.7.2-1/bitarray/__init__.py
--- 3.7.1-1/bitarray/__init__.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/__init__.py	2025-10-08 13:23:16.000000000 +0000
@@ -9,14 +9,13 @@ Please find a description of this packag
 
 Author: Ilan Schnell
 """
-from __future__ import absolute_import
 from collections import namedtuple
 
-from bitarray._bitarray import (bitarray, decodetree, _sysinfo,
-                                bits2bytes, _bitarray_reconstructor,
-                                get_default_endian, _set_default_endian,
-                                __version__)
-
+from bitarray._bitarray import (
+    bitarray, decodetree, bits2bytes, _bitarray_reconstructor,
+    get_default_endian, _set_default_endian, _sysinfo,
+    BITARRAY_VERSION as __version__
+)
 
 __all__ = ['bitarray', 'frozenbitarray', 'decodetree', 'bits2bytes']
 
diff -pruN 3.7.1-1/bitarray/_bitarray.c 3.7.2-1/bitarray/_bitarray.c
--- 3.7.1-1/bitarray/_bitarray.c	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/_bitarray.c	2025-10-08 13:23:16.000000000 +0000
@@ -1323,9 +1323,8 @@ bitarray_invert(bitarrayobject *self, Py
             return NULL;
         }
         self->ob_item[i / 8] ^= BITMASK(self, i);
-        Py_RETURN_NONE;
     }
-    if (PySlice_Check(arg)) {
+    else if (PySlice_Check(arg)) {
         Py_ssize_t start, stop, step, slicelength;
 
         if (PySlice_GetIndicesEx(arg, self->nbits,
@@ -1333,15 +1332,15 @@ bitarray_invert(bitarrayobject *self, Py
             return NULL;
         adjust_step_positive(slicelength, &start, &stop, &step);
         invert_range(self, start, stop, step);
-        Py_RETURN_NONE;
     }
-    if (arg == Py_None) {
+    else if (arg == Py_None) {
         invert_span(self, 0, self->nbits);
-        Py_RETURN_NONE;
     }
-
-    return PyErr_Format(PyExc_TypeError, "index expect, not '%s' object",
-                        Py_TYPE(arg)->tp_name);
+    else {
+        return PyErr_Format(PyExc_TypeError, "index expect, not '%s' object",
+                            Py_TYPE(arg)->tp_name);
+    }
+    Py_RETURN_NONE;
 }
 
 PyDoc_STRVAR(invert_doc,
@@ -3613,7 +3612,7 @@ endian_from_string(const char *str)
         return ENDIAN_BIG;
 
     PyErr_Format(PyExc_ValueError, "bit-endianness must be either "
-                                   "'little' or 'big', not '%s'", str);
+                 "'little' or 'big', not '%s'", str);
     return -1;
 }
 
@@ -4193,7 +4192,8 @@ sysinfo(PyObject *module, PyObject *args
     R("DEBUG", 0);
 #endif
 
-    return PyErr_Format(PyExc_KeyError, "%s", key);
+    PyErr_SetString(PyExc_KeyError, key);
+    return NULL;
 #undef R
 }
 
@@ -4287,8 +4287,8 @@ PyInit__bitarray(void)
         return NULL;
     Py_SET_TYPE(&SearchIter_Type, &PyType_Type);
 
-    PyModule_AddObject(m, "__version__",
-                       PyUnicode_FromString(BITARRAY_VERSION));
+    if (PyModule_AddStringMacro(m, BITARRAY_VERSION) < 0)
+        return NULL;
 
     return m;
 }
diff -pruN 3.7.1-1/bitarray/_util.c 3.7.2-1/bitarray/_util.c
--- 3.7.1-1/bitarray/_util.c	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/_util.c	2025-10-08 13:23:16.000000000 +0000
@@ -2254,7 +2254,8 @@ PyInit__util(void)
     Py_SET_TYPE(&CHDI_Type, &PyType_Type);
 
 #ifndef NDEBUG  /* expose segment size in debug mode for testing */
-    PyModule_AddObject(m, "_SEGSIZE", PyLong_FromSsize_t(SEGSIZE));
+    if (PyModule_AddIntConstant(m, "_SEGSIZE", SEGSIZE) < 0)
+        return NULL;
 #endif
 
     return m;
diff -pruN 3.7.1-1/bitarray/bitarray.h 3.7.2-1/bitarray/bitarray.h
--- 3.7.1-1/bitarray/bitarray.h	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/bitarray.h	2025-10-08 13:23:16.000000000 +0000
@@ -4,7 +4,7 @@
 
    Author: Ilan Schnell
 */
-#define BITARRAY_VERSION  "3.7.1"
+#define BITARRAY_VERSION  "3.7.2"
 
 #ifdef STDC_HEADERS
 #  include <stddef.h>
@@ -233,7 +233,7 @@ setup_table(char *table, char kop)
     for (k = 0; k < 256; k++) {
         char t = 0, j;
         for (j = 0; j < 8; j++) {
-            if (k & 1 << j)
+            if (k & 1 << j) {
                 /* j are the indices of active bits in k (little endian) */
                 switch (kop) {
                 case 'a': t += j;        break;  /* add active indices */
@@ -249,6 +249,7 @@ setup_table(char *table, char kop)
                 case 'r': t |= 128 >> j; break;  /* reverse bits */
                 default: Py_UNREACHABLE();
                 }
+            }
         }
         table[k] = t;
     }
diff -pruN 3.7.1-1/bitarray/test_bitarray.py 3.7.2-1/bitarray/test_bitarray.py
--- 3.7.1-1/bitarray/test_bitarray.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/test_bitarray.py	2025-10-08 13:23:16.000000000 +0000
@@ -7,8 +7,6 @@ Tests for bitarray
 
 Author: Ilan Schnell
 """
-from __future__ import absolute_import
-
 import re
 import os
 import sys
@@ -17,7 +15,7 @@ import unittest
 import shutil
 import tempfile
 from io import BytesIO, UnsupportedOperation
-from random import choice, getrandbits, randrange, randint, shuffle
+from random import choice, choices, getrandbits, randrange, randint, shuffle
 from string import whitespace
 
 # imports needed inside tests
@@ -1409,7 +1407,7 @@ class GetSequenceIndexTests(unittest.Tes
     def test_random(self):
         for a in self.randombitarrays():
             n = len(a)
-            lst = [randrange(n) for _ in range(n // 2)]
+            lst = choices(range(n), k=n//2)
             b = a[lst]
             self.assertEqual(b, bitarray(a[i] for i in lst))
             self.assertEqual(b.endian, a.endian)
@@ -1442,7 +1440,7 @@ class SetSequenceIndexTests(unittest.Tes
     def test_bool_random(self):
         for a in self.randombitarrays():
             n = len(a)
-            lst = [randrange(n) for _ in range(n // 2)]
+            lst = choices(range(n), k=n//2)
             b = a.copy()
             v = getrandbits(1)
             a[lst] = v
@@ -1476,7 +1474,7 @@ class SetSequenceIndexTests(unittest.Tes
     def test_bitarray_random(self):
         for a in self.randombitarrays():
             n = len(a)
-            lst = [randrange(n) for _ in range(n // 2)]
+            lst = choices(range(n), k=n//2)
             c = urandom_2(len(lst))
             b = a.copy()
 
@@ -1547,7 +1545,7 @@ class DelSequenceIndexTests(unittest.Tes
     def test_random(self):
         for n in range(100):
             a = urandom_2(n)
-            lst = [randrange(n) for _ in range(randint(0, n))]
+            lst = choices(range(n), k=randint(0, n))
             b = a.copy()
             del a[lst]
             self.assertEqual(len(a), n - len(set(lst)))
@@ -3233,7 +3231,7 @@ class To01Tests(unittest.TestCase, Util)
     def test_sep(self):
         for a in self.randombitarrays():
             sep = "".join(chr(randint(32, 126))
-                              for _ in range(randrange(10)))
+                          for _ in range(randrange(10)))
             self.assertEqual(a.to01(1, sep), sep.join(str(v) for v in a))
 
         a = bitarray("11100111")
@@ -3445,7 +3443,7 @@ class CountTests(unittest.TestCase, Util
     def test_sparse(self):
         n = 65536
         a = bitarray(n)
-        indices = set(randrange(n) for _ in range(256))
+        indices = set(choices(range(n), k=256))
         a[list(indices)] = 1
         self.assertEqual(a.count(1), len(indices))
         self.assertEqual(a.count(0), n - len(indices))
@@ -3636,7 +3634,7 @@ class IndexTests(unittest.TestCase, Util
         for _ in range(500):
             n = randrange(1, 200)
             a = zeros(n)
-            plst = sorted(randrange(n) for _ in range(1, 10))
+            plst = sorted(choices(range(n), k=9))
             a[plst] = 1
             # test without start and stop
             self.assertEqual(a.find(1, right=0), plst[0])
diff -pruN 3.7.1-1/bitarray/test_util.py 3.7.2-1/bitarray/test_util.py
--- 3.7.1-1/bitarray/test_util.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/test_util.py	2025-10-08 13:23:16.000000000 +0000
@@ -5,8 +5,6 @@
 """
 Tests for bitarray.util module
 """
-from __future__ import absolute_import
-
 import os
 import sys
 import math
@@ -20,8 +18,8 @@ import tempfile
 import unittest
 from io import StringIO
 from functools import reduce
-from random import (choice, getrandbits, randrange, randint, random, sample,
-                    seed)
+from random import (choice, choices, getrandbits, randrange, randint, random,
+                    sample, seed)
 from string import hexdigits, whitespace
 from collections import Counter
 
@@ -120,16 +118,6 @@ class URandomTests(unittest.TestCase):
 
 # ----------------------------  random_k()  ---------------------------------
 
-HAVE_RANDBYTES = sys.version_info[:2] >= (3, 9)
-
-@skipIf(HAVE_RANDBYTES)
-class Random_K_Not_Implemented(unittest.TestCase):
-
-    def test_not_implemented(self):
-        self.assertRaises(NotImplementedError, random_k, 100, 60)
-
-
-@skipIf(not HAVE_RANDBYTES)
 class Random_K_Tests(unittest.TestCase):
 
     def test_basic(self):
@@ -189,6 +177,8 @@ class Random_K_Tests(unittest.TestCase):
         else:
             self.fail()
 
+    # test uses math.comb, added in 3.8
+    @skipIf(sys.version_info[:2] < (3, 8))
     def test_combinations(self):
         # for entire range of 0 <= k <= n, validate that random_k()
         # generates all possible combinations
@@ -227,7 +217,8 @@ class Random_K_Tests(unittest.TestCase):
             a.append(self.collect_code_branches())
         self.assertEqual(a[0], a[2])
         self.assertEqual(a[1], a[3])
-        self.assertNotEqual(a[0], a[1])
+        for item0, item1 in zip(a[0], a[1]):
+            self.assertNotEqual(item0, item1)
         # initialize seed with current system time again
         seed()
 
@@ -341,7 +332,7 @@ class Random_P_Tests(unittest.TestCase):
     def collect_code_branches(self):
         # return list of bitarrays from all code branches of random_p()
         res = []
-        # for default p=0.5, random_p uses randbytes
+        # for default p=0.5, random_p uses getrandbits
         res.append(random_p(32))
         # test small p
         res.append(random_p(5_000, 0.002))
@@ -363,7 +354,8 @@ class Random_P_Tests(unittest.TestCase):
             a.append(self.collect_code_branches())
         self.assertEqual(a[0], a[2])
         self.assertEqual(a[1], a[3])
-        self.assertNotEqual(a[0], a[1])
+        for item0, item1 in zip(a[0], a[1]):
+            self.assertNotEqual(item0, item1)
         # initialize seed with current system time again
         seed()
 
@@ -1588,7 +1580,7 @@ class BaseTests(unittest.TestCase, Util)
         self.assertEqual(ba2base(16, a), 'f61')
 
         for n in range(50):
-            s = ''.join(choice(hexdigits) for _ in range(n))
+            s = ''.join(choices(hexdigits, k=n))
             endian = self.random_endian()
             a = base2ba(16, s, endian)
             self.assertEQUAL(a, hex2ba(s, endian))
@@ -1884,7 +1876,7 @@ class SC_Tests(unittest.TestCase, Util):
 
     def test_block_type3(self):
         a = bitarray(16_777_216, 'little')
-        a[[getrandbits(24) for _ in range(255)]] = 1
+        a[choices(range(1 << 24), k=255)] = 1
         b = bytearray([0x04, 0x00, 0x00, 0x00, 0x01, 0xc3, a.count()])
         for i in a.search(1):
             b.extend(struct.pack("<I", i)[:3])
@@ -1897,7 +1889,7 @@ class SC_Tests(unittest.TestCase, Util):
         # To understand why we cannot have a population larger than 5 for
         # an array size 4 times the size of a type 3 block, take a look
         # at the cost comparison in sc_encode_block().  (2 + 6 >= 2 * 4)
-        indices = sorted(set(randrange(len(a)) for _ in range(5)))
+        indices = sorted(set(choices(range(len(a)), k=5)))
         a[indices] = 1
         b = bytearray(b'\x04\x00\x00\x00\x04\xc4')
         b.append(len(indices))
@@ -2607,7 +2599,7 @@ class HuffmanTests(unittest.TestCase):
         self.check_tree(code)
 
     def test_random_list(self):
-        plain = [randrange(100) for _ in range(500)]
+        plain = choices(range(100), k=500)
         code = huffman_code(Counter(plain))
         a = bitarray()
         a.encode(code, plain)
@@ -2856,7 +2848,7 @@ class CanonicalHuffmanTests(unittest.Tes
 
     def ensure_round_trip(self, chc, count, symbol):
         # create a short test message, encode and decode
-        msg = [choice(symbol) for _ in range(10)]
+        msg = choices(symbol, k=10)
         a = bitarray()
         a.encode(chc, msg)
         it = canonical_decode(a, count, symbol)
diff -pruN 3.7.1-1/bitarray/util.py 3.7.2-1/bitarray/util.py
--- 3.7.1-1/bitarray/util.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/bitarray/util.py	2025-10-08 13:23:16.000000000 +0000
@@ -5,8 +5,6 @@
 """
 Useful utilities for working with bitarrays.
 """
-from __future__ import absolute_import
-
 import os
 import sys
 import math
@@ -59,15 +57,8 @@ Return (pseudo-) random bitarray of leng
 set to one.  Mathematically equivalent to setting (in a bitarray of
 length `n`) all bits at indices `random.sample(range(n), k)` to one.
 The random bitarrays are reproducible when giving Python's `random.seed()`
-with a specific seed value.
-
-This function requires Python 3.9 or higher, as it depends on the standard
-library function `random.randbytes()`.  Raises `NotImplementedError`
-when Python version is too low.
+a specific seed value.
 """
-    if sys.version_info[:2] < (3, 9):
-        raise NotImplementedError("bitarray.util.random_k() requires "
-                                  "Python 3.9 or higher")
     r = _Random(__n, endian)
     if not isinstance(k, int):
         raise TypeError("int expected, got '%s'" % type(k).__name__)
@@ -94,6 +85,7 @@ when Python version is too low.
     r = _Random(__n, endian)
     return r.random_p(p)
 
+
 class _Random:
 
     # The main reason for this class it to enable testing functionality
@@ -120,8 +112,10 @@ class _Random:
         """
         Return bitarray with each bit having probability p = 1/2 of being 1.
         """
-        # use randbytes() for reproducibility (not urandom())
-        a = bitarray(random.randbytes(self.nbytes), self.endian)
+        nbytes = self.nbytes
+        # use random module function for reproducibility (not urandom())
+        b = random.getrandbits(8 * nbytes).to_bytes(nbytes, 'little')
+        a = bitarray(b, self.endian)
         del a[self.n:]
         return a
 
diff -pruN 3.7.1-1/debian/changelog 3.7.2-1/debian/changelog
--- 3.7.1-1/debian/changelog	2025-08-31 15:08:12.000000000 +0000
+++ 3.7.2-1/debian/changelog	2025-10-29 20:02:32.000000000 +0000
@@ -1,3 +1,10 @@
+python-bitarray (3.7.2-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release.
+
+ -- Colin Watson <cjwatson@debian.org>  Wed, 29 Oct 2025 20:02:32 +0000
+
 python-bitarray (3.7.1-1) unstable; urgency=medium
 
   * Team upload.
diff -pruN 3.7.1-1/devel/resize/test_resize.py 3.7.2-1/devel/resize/test_resize.py
--- 3.7.1-1/devel/resize/test_resize.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/devel/resize/test_resize.py	2025-10-08 13:23:16.000000000 +0000
@@ -2,6 +2,7 @@ import unittest
 
 from bitarray import bitarray
 
+
 PATTERN = [0, 1, 4, 8, 16, 24, 32, 40, 48, 56, 64, 76, 88, 100, 112, 124, 136]
 
 def get_alloc(a):
@@ -56,14 +57,19 @@ class ResizeTests(unittest.TestCase):
             prev = alloc
 
     def test_no_overalloc(self):
-        # initalizing a bitarray from a list or bitarray does not overallocate
+        # initalizing a bitarray does not overallocate
         for n in range(1000):
-            a = bitarray(8 * n * [1])
-            self.assertEqual(get_alloc(a), n)
-            b = bitarray(a)
-            self.assertEqual(get_alloc(b), n)
-            c = bitarray(8 * n)
-            self.assertEqual(get_alloc(c), n)
+            blob = n * b'A'
+            for a in [
+                    bitarray(8 * n),
+                    bitarray(8 * n * [1]),
+                    bitarray(bitarray(8 * n)),
+                    bitarray(n * "00001111"),
+                    bitarray(blob),
+                    bitarray(bytearray(blob)),
+            ]:
+                self.assertEqual(len(a), 8 * n)
+                self.assertEqual(get_alloc(a), n)
 
     def test_no_overalloc_large(self):
         # starting from a large bitarray, make we sure we don't realloc each
diff -pruN 3.7.1-1/devel/test_random.py 3.7.2-1/devel/test_random.py
--- 3.7.1-1/devel/test_random.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/devel/test_random.py	2025-10-08 13:23:16.000000000 +0000
@@ -18,7 +18,7 @@ from collections import Counter
 from itertools import pairwise
 from math import comb, fmod, sqrt
 from statistics import fmean, stdev, pstdev
-from random import randint, randrange, random, binomialvariate
+from random import choices, randint, randrange, random, binomialvariate
 
 from bitarray import bitarray, frozenbitarray
 from bitarray.util import (
@@ -290,7 +290,7 @@ class Random_K_Tests(Util):
         Nm = 500_000 if HEAVY else 25_000  # number of masks
         n = 7000  # bitarray length
         # count for each array
-        ka = [randrange(1, n, 2) for _ in range(Na)]
+        ka = choices(range(1, n, 2), k=Na)
         arrays = [random_k(n, k) for k in ka]
 
         for k, a in zip(ka, arrays):  # sanity check arrays
@@ -808,7 +808,7 @@ class VerificationTests(Util):
         seq = a[a.index(1) + 1 : M]
 
         # combine random bitarrays using bitwise AND and OR operations
-        q = 0.5  # start with randbytes()
+        q = 0.5
         for k in seq:
             if k:
                 q += 0.5 * (1.0 - q)  # OR
diff -pruN 3.7.1-1/doc/changelog.rst 3.7.2-1/doc/changelog.rst
--- 3.7.1-1/doc/changelog.rst	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/doc/changelog.rst	2025-10-08 13:23:16.000000000 +0000
@@ -1,6 +1,14 @@
 Change log
 ==========
 
+**3.7.2** (2025-10-08):
+
+* enable ``util.random_k()`` for all supported Python versions,
+  previously this functions required Python 3.9 or higher
+* add official Python 3.14 support
+* update cibuildwheel to 3.2.0
+
+
 **3.7.1** (2025-08-28):
 
 * fix type hinting for memoryviews, see `#241 <https://github.com/ilanschnell/bitarray/issues/241>`__
diff -pruN 3.7.1-1/doc/random_p.rst 3.7.2-1/doc/random_p.rst
--- 3.7.1-1/doc/random_p.rst	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/doc/random_p.rst	2025-10-08 13:23:16.000000000 +0000
@@ -18,18 +18,18 @@ number of bits, using ``random.randrange
 Python 3.12 introduced ``random.binomialvariate()`` which is exactly what we
 need to determine the bitarray's population.
 
-When ``p == 0.5``, we use ``random.randbytes()`` to initialize our bitarray
+When ``p == 0.5``, we use ``random.getrandbits()`` to initialize our bitarray
 buffer.  It should be noted that ``util.urandom()`` uses ``os.urandom()``,
 but since ``util.random_p()`` is designed to give reproducible pseudo-random
-bitarrays, it uses ``randbytes()``.
+bitarrays, it uses ``getrandbits()``.
 
 Taking two (independent) such bitarrays and combining them
 using the bitwise AND operation, gives us a random bitarray with
 probability 1/4.
 Likewise, taking two bitwise OR operation gives us probability 3/4.
 Without going into too much further detail, it is possible to combine
-more than two "randbytes" bitarray to get probabilities ``i / 2**M``,
-where ``M`` is the maximal number of "randbytes" bitarrays we combine,
+more than two "getrandbits" bitarray to get probabilities ``i / 2**M``,
+where ``M`` is the maximal number of "getrandbits" bitarrays we combine,
 and ``i`` is an integer.
 The required sequence of AND and OR operations is calculated from
 the desired probability ``p`` and ``M``.
@@ -59,7 +59,7 @@ Speedup
 
 The speedup is largest, when the number of number of random numbers our
 algorithm uses is smallest.
-In the following, let ``k`` be the number of calls to ``randbytes()``.
+In the following, let ``k`` be the number of calls to ``getrandbits()``.
 For example, when ``p=0.5`` we have ``k=1``.
 When ``p`` is below our limit for using the procedure of setting individual
 bits, we call this limit ``small_p``, we have ``k=0``.
diff -pruN 3.7.1-1/doc/reference.rst 3.7.2-1/doc/reference.rst
--- 3.7.1-1/doc/reference.rst	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/doc/reference.rst	2025-10-08 13:23:16.000000000 +0000
@@ -1,7 +1,7 @@
 Reference
 =========
 
-bitarray version: 3.7.1 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
+bitarray version: 3.7.2 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
 
 In the following, ``item`` and ``value`` are usually a single bit -
 an integer 0 or 1.
@@ -379,6 +379,11 @@ This sub-module was added in version 1.2
    Also, ``a`` may be any object that exposes a writable buffer.
    Nothing about this function is specific to bitarray objects.
 
+   We should mention that Python's ``array.array`` object has a
+   method ``.byteswap()`` with similar functionality.  However, unlike
+   bitarray's ``util.byteswap()`` function, this method is limited to
+   swapping 2, 4, or 8 consecutive bytes.
+
    New in version 3.4
 
 
@@ -520,11 +525,7 @@ This sub-module was added in version 1.2
    set to one.  Mathematically equivalent to setting (in a bitarray of
    length ``n``) all bits at indices ``random.sample(range(n), k)`` to one.
    The random bitarrays are reproducible when giving Python's ``random.seed()``
-   with a specific seed value.
-
-   This function requires Python 3.9 or higher, as it depends on the standard
-   library function ``random.randbytes()``.  Raises ``NotImplementedError``
-   when Python version is too low.
+   a specific seed value.
 
    New in version 3.6
 
diff -pruN 3.7.1-1/examples/puff/_puff.c 3.7.2-1/examples/puff/_puff.c
--- 3.7.1-1/examples/puff/_puff.c	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/examples/puff/_puff.c	2025-10-08 13:23:16.000000000 +0000
@@ -32,7 +32,7 @@
 #define MAXBITS    15           /* maximum bits in a code */
 #define MAXLCODES 286           /* maximum number of literal/length codes */
 #define MAXDCODES  30           /* maximum number of distance codes */
-#define MAXCODES (MAXLCODES+MAXDCODES)  /* maximum codes lengths to read */
+#define MAXCODES (MAXLCODES + MAXDCODES)  /* maximum codes lengths to read */
 #define FIXLCODES 288           /* number of fixed literal/length codes */
 
 
@@ -74,11 +74,11 @@ static int
 decode(state_obj *s, const struct huffman *h)
 {
     Py_ssize_t nbits = s->in->nbits;
-    int len;            /* current number of bits in code */
-    int code;           /* len bits being decoded */
-    int first;          /* first code of length len */
-    int count;          /* number of codes of length len */
-    int index;          /* index of first code of length len in symbol table */
+    int len;          /* current number of bits in code */
+    int code;         /* len bits being decoded */
+    int first;        /* first code of length len */
+    int count;        /* number of codes of length len */
+    int index;        /* index of first code of length len in symbol table */
 
     if (s->incnt >= nbits) {
         PyErr_SetString(PyExc_ValueError, "no more bits to decode");
@@ -167,15 +167,15 @@ construct(struct huffman *h, const short
     int symbol;         /* current symbol when stepping through length[] */
     int len;            /* current length when stepping through h->count[] */
     int left;           /* number of possible codes left of current length */
-    short offs[MAXBITS+1];      /* offsets in symbol table for each length */
+    short offs[MAXBITS + 1];    /* offsets in symbol table for each length */
 
     /* count number of codes of each length */
     for (len = 0; len <= MAXBITS; len++)
         h->count[len] = 0;
     for (symbol = 0; symbol < n; symbol++)
-        (h->count[length[symbol]])++;   /* assumes lengths are within bounds */
-    if (h->count[0] == n)               /* no codes! */
-        return 0;                       /* complete, but decode() will fail */
+        (h->count[length[symbol]])++;  /* assumes lengths are within bounds */
+    if (h->count[0] == n)              /* no codes! */
+        return 0;                      /* complete, but decode() will fail */
 
     /* check for an over-subscribed or incomplete set of lengths */
     left = 1;                           /* one possible code of zero length */
@@ -328,7 +328,7 @@ state_extend_block(state_obj *self, PyOb
     if (nbytes >> 16)
         return PyErr_Format(PyExc_ValueError, "invalid block size: %zd",
                             nbytes);
-    if (self->incnt % 8 != 0) {
+    if (self->incnt % 8) {
         PyErr_SetString(PyExc_ValueError, "bits not aligned");
         return NULL;
     }
@@ -428,8 +428,8 @@ state_decode_block(state_obj *self, PyOb
     distcode.symbol = distsym;
     err = construct(&distcode, lengths + nlen, ndist);
     /* Fixed distance codes also have two invalid symbols that should result
-       in an error if received.  This can be implemented as an incomplete code,
-       which is why the error is ignored for fixed codes. */
+       in an error if received.  This can be implemented as an incomplete
+       code, which is why the error is ignored for fixed codes. */
     if (nlen != FIXLCODES &&
         err && (err < 0 || ndist != distcode.count[0] + distcode.count[1])) {
         PyErr_SetString(PyExc_ValueError, "incomplete distance code");
@@ -645,9 +645,11 @@ PyMODINIT_FUNC PyInit__puff(void)
     Py_INCREF((PyObject *) &state_type);
     PyModule_AddObject(m, "State", (PyObject *) &state_type);
 
-    PyModule_AddObject(m, "MAXLCODES", PyLong_FromSsize_t(MAXLCODES));
-    PyModule_AddObject(m, "MAXDCODES", PyLong_FromSsize_t(MAXDCODES));
-    PyModule_AddObject(m, "FIXLCODES", PyLong_FromSsize_t(FIXLCODES));
+#define D(n)  if ((PyModule_AddIntConstant(m, #n, n) < 0)) return NULL
+    D(MAXDCODES);
+    D(MAXLCODES);
+    D(FIXLCODES);
+#undef D
 
     return m;
 }
diff -pruN 3.7.1-1/examples/puff/test_puff.py 3.7.2-1/examples/puff/test_puff.py
--- 3.7.1-1/examples/puff/test_puff.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/examples/puff/test_puff.py	2025-10-08 13:23:16.000000000 +0000
@@ -5,7 +5,7 @@ import unittest
 
 from bitarray import bitarray
 
-from puff import State, Puff, FIXLCODES, MAXDCODES, FIXED_LENGTHS
+from puff import State, Puff, FIXLCODES, MAXDCODES, MAXLCODES, FIXED_LENGTHS
 
 
 class TestState(unittest.TestCase):
@@ -208,6 +208,8 @@ class TestFixedBlock(unittest.TestCase):
 class TestPuff(unittest.TestCase):
 
     def test_constants(self):
+        for x in MAXDCODES, MAXLCODES, FIXLCODES:
+            self.assertEqual(type(x), int)
         self.assertEqual(len(FIXED_LENGTHS), FIXLCODES + MAXDCODES)
 
     def test_align_byte_boundary(self):
diff -pruN 3.7.1-1/examples/utf-8.py 3.7.2-1/examples/utf-8.py
--- 3.7.1-1/examples/utf-8.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/examples/utf-8.py	2025-10-08 13:23:16.000000000 +0000
@@ -35,5 +35,5 @@ def code_point(u):
     print()
 
 
-for u in u'\u0024 \u00a2 \u20ac \ud55c \U00010348 \U0010ffff'.split():
+for u in '\u0024 \u00a2 \u20ac \ud55c \U00010348 \U0010ffff'.split():
     code_point(u)
diff -pruN 3.7.1-1/setup.py 3.7.2-1/setup.py
--- 3.7.1-1/setup.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/setup.py	2025-10-08 13:23:16.000000000 +0000
@@ -7,7 +7,7 @@ if sys.version_info[:3] < (3, 6, 1):
     sys.exit("""\
 ****************************************************************************
 *   bitarray requires Python 3.6.1 or later.
-*   The last version supporting Python 2 is bitarray 2.9.3.
+*   The last bitarray version supporting Python 2.7 is bitarray 2.9.3.
 ****************************************************************************
 """)
 
@@ -55,6 +55,7 @@ setup(
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
         "Programming Language :: Python :: 3.13",
+        "Programming Language :: Python :: 3.14",
         "Topic :: Utilities",
     ],
     description = "efficient arrays of booleans -- C extension",
diff -pruN 3.7.1-1/update_doc.py 3.7.2-1/update_doc.py
--- 3.7.1-1/update_doc.py	2025-08-28 20:59:12.000000000 +0000
+++ 3.7.2-1/update_doc.py	2025-10-08 13:23:16.000000000 +0000
@@ -105,6 +105,12 @@ NOTES = {
    memory (depending on the machine architecture) than the bitarray object,
    which may cause a memory error if the bitarray is very large.""",
 
+    'util.byteswap': """\
+   We should mention that Python's ``array.array`` object has a
+   method ``.byteswap()`` with similar functionality.  However, unlike
+   bitarray's ``util.byteswap()`` function, this method is limited to
+   swapping 2, 4, or 8 consecutive bytes.""",
+
     'util.gen_primes': """\
    Apart from working with prime numbers, this function is useful for
    testing, as it provides a simple way to create a well-defined bitarray
