From 7bfd8bdacca44e779ec5c87401e60e4a820163c1 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:32:14 +0200 Subject: [PATCH 1/2] refactor(testing): use A | B syntax in discriminated unions Three discriminated unions still used typing.Union inside Annotated, while the rest of the package uses the PEP 604 A | B | C syntax. Switch them to A | B | C and drop the now-unused Union import. Pydantic treats Annotated[A | B, Field(discriminator=...)] identically, so every emitted vector is byte-identical. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../test_fixtures/networking_codec.py | 28 +++++++++---------- .../test_fixtures/slot_clock.py | 4 +-- .../test_types/step_types.py | 14 ++++------ 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py b/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py index ac5f9e7d9..7af7b6c48 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py +++ b/packages/testing/src/consensus_testing/test_fixtures/networking_codec.py @@ -1,6 +1,6 @@ """Networking codec test fixture for wire-format conformance testing.""" -from typing import Annotated, ClassVar, Literal, Union +from typing import Annotated, ClassVar, Literal from pydantic import Field @@ -700,20 +700,18 @@ def attempt_decode(self) -> Exception | None: NetworkingCodec = Annotated[ - Union[ - VarintRoundtrip, - GossipTopicRoundtrip, - GossipMessageIdentifier, - GossipsubRpcRoundtrip, - ReqRespRequestRoundtrip, - ReqRespResponseRoundtrip, - ReqRespResponseStream, - EnrRoundtrip, - PeerIdentifierDerivation, - SnappyBlockRoundtrip, - SnappyFrameRoundtrip, - DecodeFailure, - ], + VarintRoundtrip + | GossipTopicRoundtrip + | GossipMessageIdentifier + | GossipsubRpcRoundtrip + | ReqRespRequestRoundtrip + | ReqRespResponseRoundtrip + | ReqRespResponseStream + | EnrRoundtrip + | PeerIdentifierDerivation + | SnappyBlockRoundtrip + | SnappyFrameRoundtrip + | DecodeFailure, Field(discriminator="kind"), ] """Discriminated union of every networking codec case under test.""" diff --git a/packages/testing/src/consensus_testing/test_fixtures/slot_clock.py b/packages/testing/src/consensus_testing/test_fixtures/slot_clock.py index b2c396f0a..406a392d2 100644 --- a/packages/testing/src/consensus_testing/test_fixtures/slot_clock.py +++ b/packages/testing/src/consensus_testing/test_fixtures/slot_clock.py @@ -1,6 +1,6 @@ """Slot clock test fixture for timing conformance testing.""" -from typing import Annotated, ClassVar, Literal, Union +from typing import Annotated, ClassVar, Literal from pydantic import Field @@ -146,7 +146,7 @@ def run(self) -> TotalIntervalsOutput: SlotClockOperation = Annotated[ - Union[FromUnixTime, FromSlot, CurrentSlot, CurrentInterval, TotalIntervals], + FromUnixTime | FromSlot | CurrentSlot | CurrentInterval | TotalIntervals, Field(discriminator="kind"), ] """Discriminated union of every slot clock conversion under test.""" diff --git a/packages/testing/src/consensus_testing/test_types/step_types.py b/packages/testing/src/consensus_testing/test_types/step_types.py index b42530df2..cb449eb4a 100644 --- a/packages/testing/src/consensus_testing/test_types/step_types.py +++ b/packages/testing/src/consensus_testing/test_types/step_types.py @@ -1,6 +1,6 @@ """Step types for fork choice tests: author-facing inputs and emitted results.""" -from typing import Annotated, Any, Literal, Union +from typing import Annotated, Any, Literal from pydantic import Field, field_serializer, model_validator @@ -173,7 +173,7 @@ class GossipAggregatedAttestationStep(BaseForkChoiceStep): # Discriminated union type for all fork choice steps ForkChoiceStep = Annotated[ - Union[TickStep, BlockStep, AttestationStep, GossipAggregatedAttestationStep], + TickStep | BlockStep | AttestationStep | GossipAggregatedAttestationStep, Field(discriminator="step_type"), ] @@ -281,11 +281,9 @@ class FilledGossipAggregatedAttestationStep(BaseFilledStep): # Discriminated union type for all emitted fork choice steps FilledForkChoiceStep = Annotated[ - Union[ - FilledTickStep, - FilledBlockStep, - FilledAttestationStep, - FilledGossipAggregatedAttestationStep, - ], + FilledTickStep + | FilledBlockStep + | FilledAttestationStep + | FilledGossipAggregatedAttestationStep, Field(discriminator="step_type"), ] From 2a30b0d89dc79ceee4c1b5d5d1786cb33a30e611 Mon Sep 17 00:00:00 2001 From: Thomas Coratger <60488569+tcoratger@users.noreply.github.com> Date: Wed, 10 Jun 2026 15:36:35 +0200 Subject: [PATCH 2/2] refactor(testing): drop duplicate abstract fork name declaration The fork name was declared abstract twice: once on the metaclass so its own methods could call cls.name(), and once as the classmethod that concrete forks override. Type the metaclass methods' cls as the fork class instead. The repr and ordering methods then resolve name through the single classmethod, so the metaclass-level declaration is no longer needed. The classmethod remains the one source of truth and still enforces that every concrete fork implements name. Repr, ordering, and abstractness are unchanged, and emitted vectors keep the same network field. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/testing/src/consensus_testing/forks/base.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/testing/src/consensus_testing/forks/base.py b/packages/testing/src/consensus_testing/forks/base.py index b93df195e..9eb971b7a 100644 --- a/packages/testing/src/consensus_testing/forks/base.py +++ b/packages/testing/src/consensus_testing/forks/base.py @@ -11,16 +11,11 @@ class BaseForkMeta(ABCMeta): For example, if ForkB inherits from ForkA, then ForkA precedes ForkB. """ - @abstractmethod - def name(cls) -> str: - """Return the name of the fork.""" - pass - - def __repr__(cls) -> str: + def __repr__(cls: "type[BaseFork]") -> str: """Print the name of the fork, instead of the class.""" return cls.name() - def __le__(cls, other: "BaseForkMeta") -> bool: + def __le__(cls: "type[BaseFork]", other: "type[BaseFork]") -> bool: """Check if this fork is older or equal to another (cls <= other).""" return cls is other or issubclass(other, cls)