Skip to content

T-17985 Handle RuntimeError from Thread.is_alive() on Python 3.14+#39

Merged
PetrHeinz merged 7 commits into
masterfrom
t-17985-python-314-is-alive-compat
Jul 1, 2026
Merged

T-17985 Handle RuntimeError from Thread.is_alive() on Python 3.14+#39
PetrHeinz merged 7 commits into
masterfrom
t-17985-python-314-is-alive-compat

Conversation

@PetrHeinz

@PetrHeinz PetrHeinz commented Jul 1, 2026

Copy link
Copy Markdown
Member

Problem

With Python 3.14.x, logtail 0.3.4 raises an uncaught RuntimeError from FlushWorker during teardown:

File "logtail/flusher.py", in step
    shutdown = not self.parent_thread.is_alive()
RuntimeError: cannot join current thread

Starting with Python 3.14, calling Thread.is_alive() on an already-terminated thread raises a RuntimeError instead of simply returning False as it did in earlier versions. The flush worker relies on self.parent_thread.is_alive() to detect that the parent thread has exited so it can flush remaining events and shut down, so on 3.14.x this surfaces as an exception during interpreter/thread teardown.

Fix

Wrap the check in a small _is_parent_alive() helper that catches RuntimeError and treats it as "parent no longer alive" (which is the correct interpretation — the exception only occurs once the thread has terminated). Both call sites in step() now go through the helper.

Tests

Added two regression tests in tests/test_flusher.py:

  • test_is_parent_alive_handles_runtime_error_is_parent_alive() returns False when is_alive() raises RuntimeError.
  • test_shutdown_condition_when_parent_is_alive_raisesstep() still flushes outstanding events and shuts down cleanly when the parent-alive check raises.

Verified locally on Python 3.14.4 (the version from the report): tests/test_flusher.py passes (9 tests).

Also manually verified end-to-end on Python 3.14.4: the example project runs clean through teardown (no RuntimeError) and all six log levels are delivered to a live source (HTTP 202, confirmed in the dashboard including the exception traceback).

CI & supported versions

The old CI matrix (Python 3.7–3.11 on ubuntu-22.04) was failing on the EOL interpreters. This PR modernizes it:

  • CI matrix → Python 3.10–3.14 on ubuntu-24.04, plus a floating '3.x' entry that always tracks the latest stable release, and a workflow_dispatch trigger for on-demand runs.
  • Dropped end-of-life Python 3.7, 3.8 and 3.9. Support declarations are kept in sync: tox.ini envlist/gh-actions mapping, the setup.py classifiers, and a new python_requires='>=3.10'.
  • Removed now-dead pre-3.10 version guards in the tests and replaced a deprecated datetime.utcfromtimestamp() call.

Housekeeping

  • Rebranded user-facing Logtail → Better Stack in prose and example messages (example project comments/log lines, example README, the flusher endpoint comment, the setup.py description). Code identifiers (LogtailHandler, the logtail package), repo URLs, the license copyright, and the "Logtail is now part of Better Stack" announcement are intentionally left unchanged.

Release

Important

This PR is the v0.4.0 release. The version has been bumped to 0.4.0 in both setup.py and logtail/__init__.py. It's a minor bump (not a 0.3.x patch) because dropping Python 3.7–3.9 support is a backwards-incompatible change. Tag v0.4.0 after merge to publish.

Fixes #38.

🤖 Generated with Claude Code

PetrHeinz and others added 5 commits July 1, 2026 14:43
Starting with Python 3.14, calling `Thread.is_alive()` on an
already-terminated thread raises `RuntimeError` instead of returning
`False`. The flush worker checks `self.parent_thread.is_alive()` to
detect parent-thread shutdown, so on Python 3.14.x this surfaces as an
uncaught `RuntimeError` during teardown.

Wrap the check in a `_is_parent_alive()` helper that treats a
`RuntimeError` as "no longer alive", and add regression tests.

Fixes T-17985.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Python 3.7-3.9 are end-of-life, and the older matrix was failing. Bump
the CI matrix to 3.10-3.14 on ubuntu-24.04 and add a workflow_dispatch
trigger so the suite can be run on demand.

Keep the out-of-CI support declarations in sync: update the tox envlist
and gh-actions mapping, refresh the setup.py Python classifiers, and add
`python_requires='>=3.10'` to make the dropped EOL versions explicit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With the minimum supported version now Python 3.10, the `version_info`
early-return in test_frame and the `skipIf(sys.version_info < (3, 8))`
guard in test_flusher are always false/never-skip. Remove them and the
now-unused `sys` / `version_info` imports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
datetime.utcfromtimestamp() is deprecated for removal. Use the
timezone-aware datetime.fromtimestamp(ts, timezone.utc), which produces
the identical UTC-aware value the assertion compares against.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
'3.x' resolves to the newest released stable Python 3, so future minor
versions are exercised as soon as they ship (today it tracks 3.14
alongside the pinned entry). Set fail-fast: false so each version's
result is reported independently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@PetrHeinz PetrHeinz changed the title Handle RuntimeError from Thread.is_alive() on Python 3.14+ T-17985 Handle RuntimeError from Thread.is_alive() on Python 3.14+ Jul 1, 2026
PetrHeinz and others added 2 commits July 1, 2026 15:34
Update user-facing product references: example project comments and log
messages, the example README prose, the flusher endpoint comment, and
the setup.py package description. Code identifiers (LogtailHandler,
LogtailContext, the logtail package), repo URLs, the license copyright,
and the "Logtail is now part of Better Stack" announcement are left
as-is.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Release v0.4.0: Python 3.14 compatibility fix plus the drop of EOL
Python 3.7-3.9 (backwards-incompatible support change -> minor bump).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@PetrHeinz PetrHeinz requested a review from adikus July 1, 2026 13:45
@PetrHeinz PetrHeinz marked this pull request as ready for review July 1, 2026 13:45

@adikus adikus left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@PetrHeinz PetrHeinz merged commit d8b1835 into master Jul 1, 2026
12 checks passed
@PetrHeinz PetrHeinz deleted the t-17985-python-314-is-alive-compat branch July 1, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incompatibility: logtail 0.3.4 with Python 3.14.4 – RuntimeError in flusher.py (is_alive)

2 participants