Skip to content

fix(utilities): fall back to the default threading excepthook before Python 3.10 (#1691) - #1701

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1691-threading-excepthook-py38
Oct 14, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1691-threading-excepthook-py38

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Please follow the guide below

  • You will be asked some questions, please read them carefully and answer honestly

  • Put an x into all the boxes [ ] relevant to your pull request (like that [x])

  • Use Preview tab to see how your pull request will actually look like

  • Searched for similar pull requests

  • Followed the coding style (make pylint, make mypy, make isort). Run on the two touched library files with the Makefile flags. isort is clean. pylint (3 messages) and mypy (1) report only lines of compat.py this PR does not touch, and main reports the same ones.

  • make test passes, and a test case covers the change. Not ticked because the whole suite was not run. The touched module's tests pass and cover the change.

  • Added a changelog entry under docs/source/changelog/ and regenerated CHANGELOG.md, if the change is user-visible: N/A — centralised in docs(changelog): shared 1.5.0 changelog — long-lived, merges last (#610, #616, #617, #618, #620) #657

What is the purpose of your pull request?

Tick the commit type your subject line carries.

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • release — bumps the version or rolls up a distribution
  • chore — anything else

Description of your pull request and other information

threading.__excepthook__ only exists from Python 3.10. Both fallbacks in _threading_excepthook therefore raised AttributeError on 3.8 and 3.9: the re-entry fallback and the delegate used when no previous hook was saved.

  • Library. pcapkit.utilities.compat re-exports the default hook as default_threading_excepthook. It is chosen by sys.version_info: threading.__excepthook__ on 3.10+, _thread._excepthook on 3.8 and 3.9, and None below 3.8. Before 3.10, CPython starts threading.excepthook as _thread._excepthook, and 3.10+ binds that same object as __excepthook__; I checked both with is. So the default is right even when a host hook was installed before import. exceptions.py imports the name and no longer probes with getattr. The name is not the stdlib __excepthook__, because imported into a module that reads as sys.__excepthook__, which takes three arguments. The utilities docs' compatibility table gets a row.
  • Tests. The excepthook child preamble reads DEFAULT_THREADING_HOOK behind a version gate, before import pcapkit. Four new tests there cover the no-previous-hook delegate, the re-entry fallback, and a hook installed before import. test_compat adds three checks. A fresh interpreter confirms the name is its startup hook, even after a host hook replaced it. The 3.5 fake asserts None. A new 3.9 fake confirms that on 3.10+ the _thread route gives threading.__excepthook__. To allow that, load_compat_as_python35 becomes load_compat_as(name, version), and WARM_BEFORE_FAKING gains _thread, builtins and functools.

Results. On main, the 4 tests named in #1691 fail on 3.8.20 and 3.9.25. With the fix, the 28 tests of test_compat and test_exceptions_excepthook pass on 3.8.20, 3.9.25, 3.10.21 and 3.14.8, under both unittest and pytest. 14 more modules that touch compat or these hooks pass on all four as well. Each new compat test fails against a mutant of one branch. On 3.14, compat.py stays at 100% coverage and exceptions.py goes from 77% to 85%.

Closes #1691

@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) review: good-to-go Cross-review at the current head says ready; CI state is separate labels Oct 14, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 9c6829407. A Sonnet cross-review reproduced the four failures from #1691 on 3.8 and 3.9 at base. On this head the module passes 21 tests on 3.8, 3.9, 3.10 and 3.14. Three mutants of the head each turn a specific new test red: reverting :355 alone, reverting :371 alone, and capturing the hook at import only. The last confirms that reading _thread._excepthook keeps 3.9 matching 3.10 when a host hook was installed earlier. The default-hook identities were checked with is on 3.8, 3.9, 3.10, 3.14 and 3.15.

@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 14, 2026
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Coverage: 89.47% (unit tier, Python 3.15, a8d248ec6, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 3 20 3 95.16%
pcapkit/const 18858 954 2366 868 91.06%
pcapkit/corekit 2372 67 762 35 96.49%
pcapkit/dumpkit 275 3 100 3 98.40%
pcapkit/foundation 3035 151 1076 34 94.28%
pcapkit/interface 138 7 52 5 93.68%
pcapkit/protocols 17166 226 4580 190 98.07%
pcapkit/toolkit 1437 279 592 5 78.12%
pcapkit/utilities 500 4 150 4 98.77%
pcapkit/vendor 4417 2342 1010 157 43.38%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw

Copy link
Copy Markdown
Owner Author

I'd actually prefer if possible, use compat module for the detect and fallback re-export.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Recorded: the default-hook detection and its fallback move into pcapkit/utilities/compat.py as a re-export, and exceptions.py imports it from there. A worker is on round 2; the verdict resets on the new head. The same preference applies to the inspect.get_annotations detection #1702 put in pcapkit/corekit/infoclass.py. It needs compat.py too, so it follows once this merges, tracked in a new issue.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: good-to-go Cross-review at the current head says ready; CI state is separate labels Oct 14, 2026
…Python 3.10 (#1691)

`threading.__excepthook__` is 3.10+, so `_threading_excepthook`'s
re-entry fallback and its no-previous-hook delegate raised
`AttributeError` on 3.8 and 3.9.

- `pcapkit.utilities.compat` re-exports the default as
  `default_threading_excepthook`, chosen by `sys.version_info`:
  `threading.__excepthook__` from 3.10, `_thread._excepthook` on 3.8
  and 3.9 (the same object), `None` below 3.8. `exceptions.py` imports
  it and keeps no feature probe of its own.
- Tests capture the default before `import pcapkit` instead of reading
  `threading.__excepthook__`, and cover both fallbacks in-process and a
  hook installed before import. `test_compat` covers each branch of the
  new name, faking 3.5 and 3.9 through a generalised `load_compat_as`.

The touched modules pass on 3.8.20, 3.9.25, 3.10.21 and 3.14.8.
@JarryShaw
JarryShaw force-pushed the fix/1691-threading-excepthook-py38 branch from 9c68294 to a8d248e Compare October 14, 2026 03:46
@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment labels Oct 14, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at a8d248ec6. A Sonnet cross-review checked round 2.

  • The change: compat.py now chooses default_threading_excepthook with sys.version_info blocks: None below 3.8, _thread._excepthook on 3.8–3.9, threading.__excepthook__ on 3.10+. exceptions.py imports it, and no getattr/hasattr probe is left for it.
  • Same behaviour: in a fresh interpreter that installs a host hook first, the name is the startup default and not the host hook, on 3.8, 3.9, 3.10 and 3.14 (checked with is).
  • Tests: the compat and excepthook modules pass 28 tests on all four interpreters. Mutants of each branch turn the new tests red.
  • Heads-up: fix(corekit,utilities): unquote a StringField's bytes on Python 3.8 through compat.unquote (#1693) #1707 also adds to compat.py's __all__ and test_compat.py's WARM_BEFORE_FAKING, so whichever merges second needs a small rebase.

@JarryShaw
JarryShaw merged commit 741883d into main Oct 14, 2026
51 checks passed
@JarryShaw JarryShaw moved this from In review to Done in PyPCAPKit Oct 14, 2026
@JarryShaw
JarryShaw deleted the fix/1691-threading-excepthook-py38 branch October 14, 2026 13:04
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 14, 2026
@JarryShaw JarryShaw added this to the PyPCAPKit v1.5 milestone Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Pull requests that fix a defect (fix: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(utilities): the threading excepthook falls back to threading.__excepthook__, which Python 3.8 and 3.9 lack

1 participant