bbangert / beaker

WSGI middleware for sessions and caching
https://beaker.readthedocs.org/
Other
517 stars 147 forks source link

dbm tests fail with Python 3.13, due to sqlite3 thread violations (Python 3.13 made a new sqlite3 backend the default for dbm) #242

Open AdamWill opened 2 months ago

AdamWill commented 2 months ago

Running beaker's test suite against Python 3.13 fails:

=================================== FAILURES ===================================
______________________________ test_dbm_container ______________________________
totaltime = 10, expiretime = None, delay = 0
    def test_dbm_container(totaltime=10, expiretime=None, delay=0):
>       _run_container_test(clsmap['dbm'], totaltime, expiretime, delay, False)
tests/test_container.py:103: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'beaker.container.DBMNamespaceManager'>, totaltime = 10
expiretime = None, delay = 0, threadlocal = False
    def _run_container_test(cls, totaltime, expiretime, delay, threadlocal):
        print("\ntesting %s for %d secs with expiretime %s delay %d" % (
            cls, totaltime, expiretime, delay))

        CachedWidget.totalcreates = 0
        CachedWidget.delay = delay

        # allow for python overhead when checking current time against expire times
        fudge = 10

        starttime = time.time()

        running = [True]
        class RunThread(Thread):
            def run(self):
                print("%s starting" % self)

                if threadlocal:
                    localvalue = Value(
                                    'test',
                                    cls('test', data_dir='./cache'),
                                    createfunc=CachedWidget,
                                    expiretime=expiretime,
                                    starttime=starttime)
                    localvalue.clear_value()
                else:
                    localvalue = value

                try:
                    while running[0]:
                        item = localvalue.get_value()
                        if expiretime is not None:
                            currenttime = time.time()
                            itemtime = item.time
                            assert itemtime + expiretime + delay + fudge >= currenttime, \
                                "created: %f expire: %f delay: %f currenttime: %f" % \
                                (itemtime, expiretime, delay, currenttime)
                        time.sleep(random.random() * .00001)
                except:
                    running[0] = False
                    raise
                print("%s finishing" % self)

        if not threadlocal:
            value = Value(
                        'test',
                        cls('test', data_dir='./cache'),
                        createfunc=CachedWidget,
                        expiretime=expiretime,
                        starttime=starttime)
            value.clear_value()
        else:
            value = None

        threads = [RunThread() for i in range(1, 8)]

        for t in threads:
            t.start()

        time.sleep(totaltime)

        failed = not running[0]
        running[0] = False

        for t in threads:
            t.join()

>       assert not failed, "One or more threads failed"
E       AssertionError: One or more threads failed
E       assert not True
tests/test_container.py:90: AssertionError
----------------------------- Captured stdout call -----------------------------
testing <class 'beaker.container.DBMNamespaceManager'> for 10 secs with expiretime None delay 0
<RunThread(Thread-8, started 139785995814592)> starting
<RunThread(Thread-9, started 139786006300352)> starting
<RunThread(Thread-10, started 139786016786112)> starting
<RunThread(Thread-11, started 139786104866496)> starting
<RunThread(Thread-12, started 139786136323776)> starting
<RunThread(Thread-13, started 139786125838016)> starting
<RunThread(Thread-14, started 139786115352256)> starting
<RunThread(Thread-8, started 139785995814592)> finishing
_____________________________ test_dbm_container_2 _____________________________
    def test_dbm_container_2():
>       test_dbm_container(expiretime=12)
tests/test_container.py:118: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_container.py:103: in test_dbm_container
    _run_container_test(clsmap['dbm'], totaltime, expiretime, delay, False)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'beaker.container.DBMNamespaceManager'>, totaltime = 10
expiretime = 12, delay = 0, threadlocal = False
    def _run_container_test(cls, totaltime, expiretime, delay, threadlocal):
        print("\ntesting %s for %d secs with expiretime %s delay %d" % (
            cls, totaltime, expiretime, delay))

        CachedWidget.totalcreates = 0
        CachedWidget.delay = delay

        # allow for python overhead when checking current time against expire times
        fudge = 10

        starttime = time.time()

        running = [True]
        class RunThread(Thread):
            def run(self):
                print("%s starting" % self)

                if threadlocal:
                    localvalue = Value(
                                    'test',
                                    cls('test', data_dir='./cache'),
                                    createfunc=CachedWidget,
                                    expiretime=expiretime,
                                    starttime=starttime)
                    localvalue.clear_value()
                else:
                    localvalue = value

                try:
                    while running[0]:
                        item = localvalue.get_value()
                        if expiretime is not None:
                            currenttime = time.time()
                            itemtime = item.time
                            assert itemtime + expiretime + delay + fudge >= currenttime, \
                                "created: %f expire: %f delay: %f currenttime: %f" % \
                                (itemtime, expiretime, delay, currenttime)
                        time.sleep(random.random() * .00001)
                except:
                    running[0] = False
                    raise
                print("%s finishing" % self)

        if not threadlocal:
            value = Value(
                        'test',
                        cls('test', data_dir='./cache'),
                        createfunc=CachedWidget,
                        expiretime=expiretime,
                        starttime=starttime)
            value.clear_value()
        else:
            value = None

        threads = [RunThread() for i in range(1, 8)]

        for t in threads:
            t.start()

        time.sleep(totaltime)

        failed = not running[0]
        running[0] = False

        for t in threads:
            t.join()

>       assert not failed, "One or more threads failed"
E       AssertionError: One or more threads failed
E       assert not True
tests/test_container.py:90: AssertionError
----------------------------- Captured stdout call -----------------------------
testing <class 'beaker.container.DBMNamespaceManager'> for 10 secs with expiretime 12 delay 0
<RunThread(Thread-43, started 139786115352256)> starting
<RunThread(Thread-44, started 139786125838016)> starting
<RunThread(Thread-45, started 139786136323776)> starting
<RunThread(Thread-46, started 139786104866496)> starting
<RunThread(Thread-47, started 139786016786112)> starting
<RunThread(Thread-48, started 139786006300352)> starting
<RunThread(Thread-49, started 139785995814592)> starting
<RunThread(Thread-43, started 139786115352256)> finishing
_____________________________ test_dbm_container_3 _____________________________
    def test_dbm_container_3():
>       test_dbm_container(expiretime=15, delay=2)
tests/test_container.py:121: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tests/test_container.py:103: in test_dbm_container
    _run_container_test(clsmap['dbm'], totaltime, expiretime, delay, False)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
cls = <class 'beaker.container.DBMNamespaceManager'>, totaltime = 10
expiretime = 15, delay = 2, threadlocal = False
    def _run_container_test(cls, totaltime, expiretime, delay, threadlocal):
        print("\ntesting %s for %d secs with expiretime %s delay %d" % (
            cls, totaltime, expiretime, delay))

        CachedWidget.totalcreates = 0
        CachedWidget.delay = delay

        # allow for python overhead when checking current time against expire times
        fudge = 10

        starttime = time.time()

        running = [True]
        class RunThread(Thread):
            def run(self):
                print("%s starting" % self)

                if threadlocal:
                    localvalue = Value(
                                    'test',
                                    cls('test', data_dir='./cache'),
                                    createfunc=CachedWidget,
                                    expiretime=expiretime,
                                    starttime=starttime)
                    localvalue.clear_value()
                else:
                    localvalue = value

                try:
                    while running[0]:
                        item = localvalue.get_value()
                        if expiretime is not None:
                            currenttime = time.time()
                            itemtime = item.time
                            assert itemtime + expiretime + delay + fudge >= currenttime, \
                                "created: %f expire: %f delay: %f currenttime: %f" % \
                                (itemtime, expiretime, delay, currenttime)
                        time.sleep(random.random() * .00001)
                except:
                    running[0] = False
                    raise
                print("%s finishing" % self)

        if not threadlocal:
            value = Value(
                        'test',
                        cls('test', data_dir='./cache'),
                        createfunc=CachedWidget,
                        expiretime=expiretime,
                        starttime=starttime)
            value.clear_value()
        else:
            value = None

        threads = [RunThread() for i in range(1, 8)]

        for t in threads:
            t.start()

        time.sleep(totaltime)

        failed = not running[0]
        running[0] = False

        for t in threads:
            t.join()

>       assert not failed, "One or more threads failed"
E       AssertionError: One or more threads failed
E       assert not True
tests/test_container.py:90: AssertionError
----------------------------- Captured stdout call -----------------------------
testing <class 'beaker.container.DBMNamespaceManager'> for 10 secs with expiretime 15 delay 2
<RunThread(Thread-50, started 139785995814592)> starting
<RunThread(Thread-51, started 139786006300352)> starting
<RunThread(Thread-52, started 139786016786112)> starting
<RunThread(Thread-53, started 139786104866496)> starting
<RunThread(Thread-54, started 139786136323776)> starting
<RunThread(Thread-55, started 139786125838016)> starting
<RunThread(Thread-56, started 139786115352256)> starting
<RunThread(Thread-50, started 139785995814592)> finishing
=============================== warnings summary ===============================
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: cover-inclusive

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: cover-package

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: detailed-errors

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: ignore-files

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: verbose

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: where

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
../../../../../usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373
  /usr/lib/python3.13/site-packages/_pytest/config/__init__.py:1373: PytestConfigWarning: Unknown config option: with-doctest

    self._warn_or_fail_if_strict(f"Unknown config option: {key}\n")
tests/test_cache.py::test_clsmap_nonexistent
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/cache.py:83: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
    import pkg_resources
tests/test_cache.py::test_clsmap_nonexistent
  /usr/lib/python3.13/site-packages/pkg_resources/__init__.py:2833: DeprecationWarning: Deprecated call to `pkg_resources.declare_namespace('paste')`.
  Implementing implicit namespace packages (as specified in PEP 420) is preferred to `pkg_resources.declare_namespace`. See https://setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages
    declare_namespace(pkg)
tests/test_cache.py::test_legacy_cache
tests/test_cache.py::test_legacy_cache
tests/test_cache.py::test_legacy_cache
tests/test_cache.py::test_legacy_cache
tests/test_cache.py::test_legacy_cache
tests/test_cache.py::test_legacy_cache
tests/test_cache.py::test_legacy_cache
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/cache.py:335: DeprecationWarning: Specifying a 'type' and other namespace configuration with cache.get()/put()/etc. is deprecated. Specify 'type' and other namespace configuration to cache_manager.get_cache() and/or the Cache constructor instead.
    return self._legacy_get_value(key, **kw)
tests/test_container.py::test_dbm_container
  /usr/lib/python3.13/site-packages/_pytest/threadexception.py:73: PytestUnhandledThreadExceptionWarning: Exception in thread Thread-14

  Traceback (most recent call last):
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 80, in _execute
      return closing(self._cx.execute(*args, **kwargs))
                     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139785995814592 and this is thread id 139786115352256.

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 334, in get_value
      has_value = self.has_value()
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 296, in has_value
      return self.key in self.namespace
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 605, in __contains__
      return key in self.dbm
             ^^^^^^^^^^^^^^^
    File "<frozen _collections_abc>", line 813, in __contains__
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 90, in __getitem__
      with self._execute(LOOKUP_KEY, (key,)) as cu:
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 82, in _execute
      raise error(str(exc))
  dbm.sqlite3.error: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139785995814592 and this is thread id 139786115352256.

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/usr/lib64/python3.13/threading.py", line 1039, in _bootstrap_inner
      self.run()
      ~~~~~~~~^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_container.py", line 53, in run
      item = localvalue.get_value()
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 347, in get_value
      self.namespace.release_read_lock()
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 215, in release_read_lock
      self.close(checkcount=True)
      ~~~~~~~~~~^^^^^^^^^^^^^^^^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 254, in close
      self.do_close()
      ~~~~~~~~~~~~~^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 590, in do_close
      self.dbm.close()
      ~~~~~~~~~~~~~~^^
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 114, in close
      self._cx.close()
      ~~~~~~~~~~~~~~^^
  sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139785995814592 and this is thread id 139786115352256.

    warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg))
tests/test_container.py::test_dbm_container_2
  /usr/lib/python3.13/site-packages/_pytest/threadexception.py:73: PytestUnhandledThreadExceptionWarning: Exception in thread Thread-46

  Traceback (most recent call last):
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 80, in _execute
      return closing(self._cx.execute(*args, **kwargs))
                     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139786115352256 and this is thread id 139786104866496.

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/usr/lib64/python3.13/threading.py", line 1039, in _bootstrap_inner
      self.run()
      ~~~~~~~~^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_container.py", line 53, in run
      item = localvalue.get_value()
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 334, in get_value
      has_value = self.has_value()
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 296, in has_value
      return self.key in self.namespace
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 605, in __contains__
      return key in self.dbm
             ^^^^^^^^^^^^^^^
    File "<frozen _collections_abc>", line 813, in __contains__
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 90, in __getitem__
      with self._execute(LOOKUP_KEY, (key,)) as cu:
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 82, in _execute
      raise error(str(exc))
  dbm.sqlite3.error: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139786115352256 and this is thread id 139786104866496.

    warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg))
tests/test_container.py::test_dbm_container_3
  /usr/lib/python3.13/site-packages/_pytest/threadexception.py:73: PytestUnhandledThreadExceptionWarning: Exception in thread Thread-51

  Traceback (most recent call last):
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 80, in _execute
      return closing(self._cx.execute(*args, **kwargs))
                     ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139785995814592 and this is thread id 139786006300352.

  During handling of the above exception, another exception occurred:

  Traceback (most recent call last):
    File "/usr/lib64/python3.13/threading.py", line 1039, in _bootstrap_inner
      self.run()
      ~~~~~~~~^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_container.py", line 53, in run
      item = localvalue.get_value()
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 334, in get_value
      has_value = self.has_value()
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 296, in has_value
      return self.key in self.namespace
             ^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/container.py", line 605, in __contains__
      return key in self.dbm
             ^^^^^^^^^^^^^^^
    File "<frozen _collections_abc>", line 813, in __contains__
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 90, in __getitem__
      with self._execute(LOOKUP_KEY, (key,)) as cu:
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
    File "/usr/lib64/python3.13/dbm/sqlite3.py", line 82, in _execute
      raise error(str(exc))
  dbm.sqlite3.error: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139785995814592 and this is thread id 139786006300352.

    warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg))
tests/test_cookie_only.py::test_expires
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/session.py:278: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
    expires_date = datetime.utcnow() + expires
tests/test_cookie_only.py::test_changing_encrypt_key_with_timeout
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/session.py:785: BeakerWarning: Invalidating corrupt session 80c14a8fd21d46dfa448db87028f43e4; error was: invalid load key, '\x05'..  Set invalidate_corrupt=False to propagate this exception.
    self.__dict__['_sess'] = session_cls(req, **params)
tests/test_cookie_only.py::test_cookie_path_properly_set_after_delete
tests/test_session.py::test_session
tests/test_session.py::test_session
tests/test_session.py::test_cookies_enabled
tests/test_session.py::test_load_deleted_from_storage_session__not_loaded
tests/test_session.py::TestSessionObject::test_no_autosave_saves_with_delete
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/beaker/session.py:378: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
    expires = datetime.utcnow() - timedelta(365)
tests/test_increment.py::test_load_session_by_id
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_increment.py:160: DeprecationWarning: 'count' is passed as positional argument
    old_id = re.sub(r'^.*?session id is (\S+)$', r'\1', res.body.decode('utf-8'), re.M)
tests/test_session.py::test_session
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_session.py:24: BeakerWarning: Invalidating corrupt session 2cf2d7311441465789a54a822f703d89; error was: invalid load key, '\x19'..  Set invalidate_corrupt=False to propagate this exception.
    return Session({}, **options)
tests/test_session.py::test_session
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_session.py:40: BeakerWarning: Invalidating corrupt session 2cefcd4b09214ab28ef4161bba0ba912; error was: invalid load key, '\xc5'..  Set invalidate_corrupt=False to propagate this exception.
    return CookieSession(COOKIE_REQUEST, **options)
tests/test_session.py::test_invalidate_corrupt
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_session.py:24: BeakerWarning: Invalidating corrupt session b1dc86ac903343bfa07744c3b1bec89b; error was: pickle data was truncated.  Set invalidate_corrupt=False to propagate this exception.
    return Session({}, **options)
tests/test_session.py::test_invalidate_invalid_signed_cookie_invalidate_corrupt
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_session.py:40: BeakerWarning: Invalidating corrupt session 7d76e0b24d9b42478f667a963469c97c; error was: Invalid signature.  Set invalidate_corrupt=False to propagate this exception.
    return CookieSession(COOKIE_REQUEST, **options)
tests/test_managers/test_ext_redis.py::TestRedis::test_createfunc
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_managers/base.py:270: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
    begin = datetime.datetime.utcnow()
tests/test_managers/test_ext_redis.py::TestRedis::test_createfunc
  /builddir/build/BUILD/python-beaker-1.12.1-build/beaker-1.12.1/tests/test_managers/base.py:281: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
    assert datetime.datetime.utcnow() - begin > datetime.timedelta(seconds=1)
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html

Note in the captured warnings, we see several cases of sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 139786115352256 and this is thread id 139786104866496., which seem to be what is causing some threads to fail, hence the test to fail.

Python 3.13 changes doc notes: "Add new dbm.sqlite3 backend, and make it the default dbm backend."