From fde0337d32843cc47573dd57d4554d2aa40ba428 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Fri, 18 Sep 2026 12:19:38 -0400 Subject: [PATCH] corekit: narrow the interface fields' post_process return type via isinstance, not cast (#473) - IPv4InterfaceField.post_process and IPv6InterfaceField.post_process each built their return value with the polymorphic ipaddress.ip_interface(), typed IPv4Interface | IPv6Interface, against a declared return type that admits only one of the two -- two standing mypy [return-value] errors. - The two ip_interface() calls in the matching pre_process methods already wore a # type: ignore[assignment] for the identical reason, confirmed by removing the suppressions and re-running mypy (same errors, different code). - Fixed both pairs the same way: replace the `val.version != self.version` comparison with `not isinstance(val, IPv4Interface/IPv6Interface)`, which mypy accepts as a narrowing type guard. Rejected constructing the concrete class directly (ipaddress.IPv4Interface(...)/IPv6Interface(...)) instead, because it changes behaviour: passing an already-constructed interface of the *other* concrete type to pre_process would then raise before reaching the version check, changing the message from "IP version mismatch: ..." to "invalid IP interface: ...", breaking test_wrong_version_message_is_not_relabelled_as_a_malformed_value. - No behaviour change: probed both versions side by side on 16 representative inputs (round trips at every extreme prefix length, wrong-version strings, wrong-version pre-built Interface objects, malformed values, a non- contiguous IPv4 netmask, an out-of-range IPv6 prefix length) -- 0 mismatches in output or exception message. mypy pcapkit: 125 errors/40 files -> 123 errors/39 files, diffed line by line; the only errors removed are the two named in #473, none added. pytest tests/ (excluding files owned by other in-flight PRs): 963 passed, 17 skipped, 873 subtests passed, 0 failures. --- pcapkit/corekit/fields/ipaddress.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/pcapkit/corekit/fields/ipaddress.py b/pcapkit/corekit/fields/ipaddress.py index b2d144695e..f4907de25d 100644 --- a/pcapkit/corekit/fields/ipaddress.py +++ b/pcapkit/corekit/fields/ipaddress.py @@ -242,9 +242,10 @@ def pre_process(self, value: 'IPv4Interface | bytes | int | str', packet: 'dict[ val = value else: with _reraise_as_field_value_error('invalid IP interface'): - val = ipaddress.ip_interface(value) # type: ignore[assignment] - if val.version != self.version: - raise FieldValueError(f'IP version mismatch: {val.version} != {self.version}') + parsed = ipaddress.ip_interface(value) + if not isinstance(parsed, ipaddress.IPv4Interface): + raise FieldValueError(f'IP version mismatch: {parsed.version} != {self.version}') + val = parsed ip = val.ip mask = val.netmask @@ -281,7 +282,7 @@ def post_process(self, value: 'bytes', packet: 'dict[str, Any]') -> 'IPv4Interfa with _reraise_as_field_value_error('invalid IPv4 interface'): val = ipaddress.ip_interface(f'{ip}/{mask}') - if val.version != self.version: + if not isinstance(val, ipaddress.IPv4Interface): raise FieldValueError(f'IP version mismatch: {val.version} != {self.version}') return val @@ -326,9 +327,10 @@ def pre_process(self, value: 'IPv6Interface | bytes | int | str', packet: 'dict[ val = value else: with _reraise_as_field_value_error('invalid IP interface'): - val = ipaddress.ip_interface(value) # type: ignore[assignment] - if val.version != self.version: - raise FieldValueError(f'IP version mismatch: {val.version} != {self.version}') + parsed = ipaddress.ip_interface(value) + if not isinstance(parsed, ipaddress.IPv6Interface): + raise FieldValueError(f'IP version mismatch: {parsed.version} != {self.version}') + val = parsed ip = val.ip prefixlen = cast('int', val._prefixlen) # type: ignore[attr-defined] # pylint: disable=protected-access @@ -372,6 +374,6 @@ def post_process(self, value: 'bytes', packet: 'dict[str, Any]') -> 'IPv6Interfa with _reraise_as_field_value_error('invalid IPv6 interface'): val = ipaddress.ip_interface(f'{ip}/{prefixlen}') - if val.version != self.version: + if not isinstance(val, ipaddress.IPv6Interface): raise FieldValueError(f'IP version mismatch: {val.version} != {self.version}') return val