Skip to content

fix(corekit): read only a class's own annotations when building an Info constructor (#1692) - #1702

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1692-infoclass-own-annotations
Oct 14, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1692-infoclass-own-annotations

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Please follow the guide below

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

Before 3.10 (bpo-43901), a class with no annotations of its own returns its nearest base's from cls.__annotations__. The new _own_annotations reads the class's own __dict__ below 3.10 and uses inspect.get_annotations from 3.10 on. It cannot use __dict__ alone there, because 3.14 keeps lazily evaluated annotations out of __dict__; this is the same split as ProtocolBase in protocols/protocol.py. info_final and _nested_types both call it now.

  • A field-less subclass, and a field-less subclass of that, now get (self, dict_=None, **kwargs) on every version. A diamond whose middle class is field-less now gets (self, a, c, d); before this fix 3.8 and 3.9 gave (self, c, a, d).
  • from_dict now rebuilds an Info field that a field-less subclass inherits. Before, 3.8 and 3.9 resolved that field's annotation in the subclass's namespace and skipped it.
  • Across pcapkit's 494 Info classes, the old and new readers differ only for the four _MultiInfo classes, and those generate no __init__. So no shipped class changes.
  • The four cls.__annotations__[...] writes (protocols/data/internet/ipv4.py:74, :150, :345, ipv6.py:76) were measured on 3.8, 3.9, 3.10 and 3.14. Each target class declares its own annotations, and nothing leaked into any base. They are unchanged.

Tests: the three tests from the issue and the new OwnAnnotationsTests fail on origin/main under 3.8 and 3.9. With this fix they pass on 3.8, 3.9, 3.10 and 3.14, under both pytest and unittest. 15 related modules pass on every version. The one exception is test_final_enforcement.py::VersionPortabilityTests on 3.8, which already fails on main (ast.unparse). infoclass.py coverage from this test module goes from 66% to 72% on 3.14.

Closes #1692

…fo constructor (#1692)

Below Python 3.10 (bpo-43901), cls.__annotations__ of a class declaring
none is its nearest base's. info_final therefore built a field-less Info
subclass's __init__ from Info's own __finalised__, __additional__ and
__excluded__ on 3.6-3.9, and _nested_types resolved an inherited
annotation in the subclass's namespace rather than the declaring class's.

- infoclass: _own_annotations reads a class's own __dict__ below 3.10 and
  inspect.get_annotations from 3.10 on, where 3.14's lazy annotations are
  not in __dict__. info_final and _nested_types both use it.
- tests: OwnAnnotationsTests pins the generated signature of a field-less
  subclass, a subclass of one, a class with fields and a diamond, plus a
  from_dict rebuild through a field-less subclass; the signature check
  also runs through the pre-3.10 reader on every version.

The issue's three tests and the new ones fail on origin/main under 3.8
and 3.9 and pass here on 3.8, 3.9, 3.10 and 3.14.
@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 67ee18a25. A Sonnet cross-review reproduced the three failures from #1692 on 3.8 and 3.9 at base. On this head they pass on 3.8, 3.9, 3.10 and 3.14, with 23 OK under unittest. The new OwnAnnotationsTests fail against the base on 3.8 and 3.9. Mutants that revert only info_final, or only _nested_types, each fail their own tests. No regression was found on 3.10 or 3.14.

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

Copy link
Copy Markdown
Contributor

Coverage: 89.47% (unit tier, Python 3.15, 67ee18a25, 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 494 4 146 4 98.75%
pcapkit/vendor 4417 2342 1010 157 43.38%

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

@JarryShaw
JarryShaw merged commit 375d37a into main Oct 14, 2026
54 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 14, 2026
@JarryShaw
JarryShaw deleted the fix/1692-infoclass-own-annotations branch October 14, 2026 02:45
@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(corekit): before Python 3.10 an Info subclass with no annotations of its own inherits Info's, so its generated __init__ takes the wrong arguments

1 participant