Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@ name: "Lint"
# that rewrites the generated constants, not as a check.
#
# Every step but actionlint is advisory (`continue-on-error: true`), because none
# is clean and a job red from day one teaches everyone to ignore red. Promote a
# step by deleting its `continue-on-error` once it is clean, and move it below
# the advisory steps, as a failed step skips every step after it. Counts are in
# the run summary, not here: written-down counts drift (#753).
# was clean when added, and a job red from day one teaches everyone to ignore
# red. Promote a step by deleting its `continue-on-error` once it is clean, and
# move it below the advisory steps, as a failed step skips every step after it.
# Counts are in the run summary, not here: written-down counts drift (#753).
#
# pylint's `R0401` cyclic-import depends on module-processing order, so runs on
# one tree disagree on it; a change in the R class alone is not evidence.
#
# Promotion order, cheapest first:
# * bandit -- mostly B104 on `'0.0.0.0'` default arguments in MH/MIP option
# builders, which bind no socket; triage the rest with `# nosec`.
# * mypy -- part of its output is `unused-ignore`, risk-free to delete.
# * vermin -- blocked on a decision, not on cleanup: see its step.
# * bandit -- clean since #2145: each finding was a false positive and carries
# a justified `# nosec`. Promotable.
# * vermin -- clean since #2145: meets vermin.ini's 3.8 target. Promotable.
# * mypy -- #2145 cleared the `unused-ignore`s and the pyshark link-type
# tables; the rest is real typing work.
# * pylint -- not realistically promotable: `unused-argument`,
# `redefined-builtin` and `super-init-not-called` are inherent to the
# schema DSL, and its metaprogramming yields `unsubscriptable-object` and
Expand Down Expand Up @@ -127,11 +128,10 @@ jobs:
# `vermin-ci`, not `vermin`: that target redirects the report into temp/,
# out of this log. Same flags, via $(VERMIN_FLAGS).
#
# Blocking needs a decision, not a cleanup. vermin.ini's `targets = 3.8`
# matches `requires-python` (#2051), but vermin reports 3.11:
# `--no-parse-comments` ignores the `# novermin` markers on compat.py's
# version-gated imports, and evaluated annotations put some
# `pcapkit.foundation` modules at 3.9.
# vermin.ini's `targets = 3.8` matches `requires-python` (#2051). Comments
# are parsed (#2145), so the `# novermin` markers on compat.py's
# version-gated imports count, and builtin generics in evaluated
# annotations are spelt with `typing` aliases.
- name: vermin
id: vermin
continue-on-error: true
Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,11 @@ RUN ?= pipenv run
# flags in each. Note the `pcapkit` before the flags as well as after: that is how this
# recipe has always read, and vermin de-duplicates the paths (it reports the
# same number of files analyzed either way), so it is preserved verbatim rather
# than tidied.
VERMIN_FLAGS = --backport argparse --backport enum --backport importlib --backport ipaddress --backport typing --backport typing_extensions --no-parse-comments --eval-annotations -vv
# than tidied. `--no-parse-comments` is gone (#2145): it came with the first
# vermin recipe, with no recorded reason, and made vermin ignore the
# `# novermin` markers on compat.py's version-gated imports. The cost of parsing
# them is that a marker silences its whole line, so keep them on guarded lines.
VERMIN_FLAGS = --backport argparse --backport enum --backport importlib --backport ipaddress --backport typing --backport typing_extensions --eval-annotations -vv
# `pylint.extensions.emptystring` no longer exists (#767): the check it provided,
# `compare-to-empty-string`, was folded into the core `refactoring` checker at
# pylint 3.0 as `use-implicit-booleaness-not-comparison-to-string` (C1804), with
Expand Down Expand Up @@ -180,7 +183,7 @@ vermin:
# that would have opened it never reached -- useless as a log.
# This one writes to stdout and lets vermin's failure propagate, which is what
# `targets = 3.8` in vermin.ini is for: vermin exits non-zero when the target is
# not met, and it detects 3.11.
# not met.
vermin-ci:
$(RUN) vermin pcapkit $(VERMIN_FLAGS) pcapkit

Expand Down
2 changes: 1 addition & 1 deletion pcapkit/const/ftp/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,5 +668,5 @@ def _missing_(cls, value: 'str') -> 'Command':
raise ValueError(f'{value!r} is not a valid {cls.__name__}')
name = value.upper()
if name in cls._member_map_:
return cls._member_map_[name] # type: ignore[return-value]
return cls._member_map_[name]
return cls._unregistered_member(value, name)
2 changes: 1 addition & 1 deletion pcapkit/const/http/method.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,5 +329,5 @@ def _missing_(cls, value: 'str') -> 'Method':
raise ValueError(f'{value!r} is not a valid {cls.__name__}')
name = value.upper()
if name in cls._member_map_:
return cls._member_map_[name] # type: ignore[return-value]
return cls._member_map_[name]
return cls._unregistered_member(value, name)
6 changes: 3 additions & 3 deletions pcapkit/corekit/fields/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def optional(self) -> 'bool':
def protocol(self) -> 'Type[_TP]':
"""Payload protocol."""
if self._protocol is None:
from pcapkit.protocols.misc.raw import Raw # type: ignore[unreachable] # pylint: disable=import-outside-toplevel # isort:skip
from pcapkit.protocols.misc.raw import Raw # pylint: disable=import-outside-toplevel # isort:skip
return Raw
return self._protocol

Expand Down Expand Up @@ -391,7 +391,7 @@ def unpack(self, buffer: 'bytes | IO[bytes]', packet: 'dict[str, Any]') -> '_TP'

"""
if self._protocol is None:
if isinstance(buffer, bytes): # type: ignore[unreachable]
if isinstance(buffer, bytes):
return cast('_TP', buffer)
return cast('_TP', buffer.read())

Expand Down Expand Up @@ -458,7 +458,7 @@ def field(self) -> 'FieldBase[_TC]':
"""Field instance."""
return self._field

def __init__(self, selector: 'Callable[[dict[str, Any]], FieldBase[_TC]]' = lambda _: NoValueField()) -> 'None': # type: ignore[assignment,return-value]
def __init__(self, selector: 'Callable[[dict[str, Any]], FieldBase[_TC]]' = lambda _: NoValueField()) -> 'None':
#self._name = '<switch>'
self._field = cast('FieldBase[_TC]', NoValueField())
self._selector = selector
Expand Down
6 changes: 3 additions & 3 deletions pcapkit/corekit/infoclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,7 @@ class MultiInfo(_MultiInfo[KT, VT], MultiDict[KT, VT]):
__hash__ = None # type: ignore[assignment]
__repr__ = MultiDict.__repr__
__str__ = MultiDict.__str__
__copy__ = MultiDict.__copy__ # type: ignore[assignment]
__copy__ = MultiDict.__copy__
keys = MultiDict.keys
values = MultiDict.values
items = MultiDict.items # type: ignore[assignment]
Expand Down Expand Up @@ -1104,7 +1104,7 @@ class OrderedMultiInfo(_MultiInfo[KT, VT], OrderedMultiDict[KT, VT]):
__hash__ = None
__repr__ = OrderedMultiDict.__repr__
__str__ = OrderedMultiDict.__str__
__copy__ = OrderedMultiDict.__copy__ # type: ignore[assignment]
__copy__ = OrderedMultiDict.__copy__
keys = OrderedMultiDict.keys
values = OrderedMultiDict.values
items = OrderedMultiDict.items # type: ignore[assignment]
Expand Down Expand Up @@ -1301,7 +1301,7 @@ def _nested_types(cls: 'Type[Info]') -> 'dict[str, Type[Info]]':
try:
hint = typing.get_type_hints(types.SimpleNamespace(__annotations__={key: ann}),
globalns, localns)[key]
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except # nosec: B112 # unresolvable: not nested
continue

info_cls = _as_info_type(hint)
Expand Down
18 changes: 9 additions & 9 deletions pcapkit/corekit/multidict.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def __getitem__(self, key: '_KT') -> '_VT':
if key in self:
lst = super().__getitem__(key)
if len(lst) > 0:
return lst[0] # type: ignore[no-any-return]
return lst[0]
raise MissingKeyError(key, quiet=True)

def __setitem__(self, key: '_KT', value: '_VT') -> 'None':
Expand All @@ -216,7 +216,7 @@ def get(self, key: '_KT') -> '_VT | _T': ...
@overload
def get(self, key: '_KT', default: '_VT | _T' = ...) -> '_VT | _T': ...

def get(self, key: '_KT', default: '_VT | _T' = None) -> '_VT | _T': # type: ignore[assignment,misc]
def get(self, key: '_KT', default: '_VT | _T' = None) -> '_VT | _T': # type: ignore[assignment]
try:
return self[key]
except MissingKeyError:
Expand Down Expand Up @@ -291,7 +291,7 @@ def setlistdefault(self, key: '_KT', default_list: 'Optional[Iterable[_VT]]' = N
if key not in self:
super().__setitem__(key, list(default_list or ()))

return super().__getitem__(key) # type: ignore[no-any-return]
return super().__getitem__(key)

def items(self, multi: 'bool' = False) -> 'Iterable[tuple[_KT, _VT]]': # type: ignore[override]
"""Return an iterator of ``(key, value)`` pairs.
Expand Down Expand Up @@ -390,7 +390,7 @@ def __or__(self, other: 'Mapping[_KT, _VT | list[_VT] | tuple[_VT, ...] | set[_V
rv.update(other)
return rv

def __ior__(self, other: 'Mapping[_KT, _VT | list[_VT] | tuple[_VT, ...] | set[_VT]] | Iterable[tuple[_KT, _VT]]') -> 'Self': # type: ignore[override,misc] # pylint: disable=line-too-long
def __ior__(self, other: 'Mapping[_KT, _VT | list[_VT] | tuple[_VT, ...] | set[_VT]] | Iterable[tuple[_KT, _VT]]') -> 'Self': # type: ignore[override] # pylint: disable=line-too-long
if not isinstance(other, (cabc.Mapping, cabc.Iterable)):
return NotImplemented

Expand All @@ -404,7 +404,7 @@ def pop(self, key: '_KT', default: '_VT') -> '_VT': ... # pylint: disable=argum
@overload
def pop(self, key: '_KT', default: '_T') -> '_VT | _T': ... # pylint: disable=arguments-differ

def pop(self, key: '_KT', default: '_VT | _T' = _missing) -> '_VT | _T': # type: ignore[assignment,misc]
def pop(self, key: '_KT', default: '_VT | _T' = _missing) -> '_VT | _T': # type: ignore[assignment]
"""Pop the first item for a list on the dict. Afterwards the
key is removed from the dict, so additional values are discarded:

Expand Down Expand Up @@ -451,7 +451,7 @@ def poplist(self, key: '_KT') -> 'list[_VT]':
"""Pop the list for a key from the dict. If the key is not in the dict
an empty list is returned.
"""
return super().pop(key, []) # type: ignore[no-any-return]
return super().pop(key, [])

def popitemlist(self) -> 'tuple[_KT, list[_VT]]':
"""Pop a ``(key, list)`` tuple from the dict."""
Expand Down Expand Up @@ -551,7 +551,7 @@ def __setstate__(self, values: 'Any') -> 'None':

def __getitem__(self, key: '_KT') -> '_VT':
if key in self:
return dict.__getitem__(self, key)[0].value # type: ignore[no-any-return]
return dict.__getitem__(self, key)[0].value
raise MissingKeyError(key, quiet=True)

def __setitem__(self, key: '_KT', value: '_VT') -> 'None':
Expand Down Expand Up @@ -598,7 +598,7 @@ def listvalues(self) -> 'Iterable[list[_VT]]':
yield values

def add(self, key: '_KT', value: '_VT') -> 'None':
dict.setdefault(self, key, []).append(_omd_bucket(self, key, value)) # type: ignore[misc]
dict.setdefault(self, key, []).append(_omd_bucket(self, key, value))

def getlist(self, key: '_KT') -> 'list[_VT]':
rv: 'list[_omd_bucket[_KT, _VT]]'
Expand Down Expand Up @@ -634,7 +634,7 @@ def pop(self, key: '_KT', default: '_VT') -> '_VT': ... # pylint: disable=argum
@overload
def pop(self, key: '_KT', default: '_T') -> '_VT | _T': ... # pylint: disable=arguments-differ

def pop(self, key: '_KT', default: '_VT | _T' = _missing) -> '_VT | _T': # type: ignore[assignment,misc]
def pop(self, key: '_KT', default: '_VT | _T' = _missing) -> '_VT | _T': # type: ignore[assignment]
buckets: 'list[_omd_bucket[_KT, _VT]]'

try:
Expand Down
2 changes: 1 addition & 1 deletion pcapkit/dumpkit/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import re
import string
import types
import xml.sax.saxutils
import xml.sax.saxutils # nosec: B406 # escape() only, nothing parsed
from typing import TYPE_CHECKING

import aenum
Expand Down
2 changes: 1 addition & 1 deletion pcapkit/foundation/engines/_pcap_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def installed_distributions() -> 'tuple[str, ...]':
try:
if next(iter(importlib.metadata.distributions(name=name)), None) is None:
continue
except Exception: # pylint: disable=broad-except
except Exception: # pylint: disable=broad-except # nosec: B112 # see below
# A corrupt or unreadable ``dist-info`` is not a reason to fail an
# extraction. Detection is a courtesy; treat "cannot tell" as "not
# installed" and let the import attempt be the authority.
Expand Down
4 changes: 2 additions & 2 deletions pcapkit/foundation/engines/pcap_ct.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
__all__ = ['PCAP_CT']

if TYPE_CHECKING:
from typing import Iterator, Optional, Protocol, Type
from typing import Iterator, Optional, Protocol, Tuple, Type

from pcap import pcap as Handle

Expand All @@ -39,7 +39,7 @@ def pcap(self) -> 'Type[Handle]': ...
#: so that it is not mistaken for
#: :class:`pcapkit.protocols.misc.pcap.frame.Frame`, which is what the
#: built-in engines return.
RawFrame = tuple[float, bytes]
RawFrame = Tuple[float, bytes]


class PCAP_CT(EngineBase['RawFrame']):
Expand Down
4 changes: 2 additions & 2 deletions pcapkit/foundation/engines/pypcap.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
__all__ = ['PyPCAP']

if TYPE_CHECKING:
from typing import Iterator, Optional, Protocol, Type
from typing import Iterator, Optional, Protocol, Tuple, Type

from pcap import pcap as Handle

Expand All @@ -44,7 +44,7 @@ def pcap(self) -> 'Type[Handle]': ...
#: iteration yields. Deliberately *not* named ``Frame``, so that it is not
#: mistaken for :class:`pcapkit.protocols.misc.pcap.frame.Frame`, which is
#: what the built-in engines return.
RawFrame = tuple[float, bytes]
RawFrame = Tuple[float, bytes]


class PyPCAP(EngineBase['RawFrame']):
Expand Down
5 changes: 3 additions & 2 deletions pcapkit/foundation/extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
if TYPE_CHECKING:
from io import BufferedReader
from types import ModuleType, TracebackType
from typing import IO, Any, Callable, DefaultDict, Iterable, Mapping, Optional, Type, Union
from typing import (IO, Any, Callable, DefaultDict, Iterable, Mapping, Optional, Tuple, Type,
Union)

from dpkt.dpkt import Packet as DPKTPacket
from pcapfile.structs import pcap_packet as PCAPFilePacket
Expand Down Expand Up @@ -84,7 +85,7 @@
# One member covers both: they read the same interface, from two independent
# distributions of it.
Packet = Union[Frame, PCAPNG, ScapyPacket, DPKTPacket, PySharkPacket,
PCAPFilePacket, tuple[float, bytes]]
PCAPFilePacket, Tuple[float, bytes]]

Protocols = Union[str, ProtocolBase, Type[ProtocolBase]]
# NOTE: every caller discards the callback's result, so it is typed ``Any``:
Expand Down
4 changes: 2 additions & 2 deletions pcapkit/foundation/reassembly/reassembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@
_BT = TypeVar('_BT', bound='Info')

if TYPE_CHECKING:
from typing import Any, Callable, Optional, Type
from typing import Any, Callable, List, Optional, Type

from typing_extensions import Self

from pcapkit.corekit.infoclass import Info
from pcapkit.corekit.module import ModuleDescriptor
from pcapkit.protocols.protocol import ProtocolBase

CallbackFn = Callable[[list[_DT]], None]
CallbackFn = Callable[[List[_DT]], None]

__all__ = ['Reassembly']

Expand Down
2 changes: 1 addition & 1 deletion pcapkit/protocols/internet/internet.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def _decode_next_layer(self, dict_: '_PT', proto: 'Optional[int]' = None, # pyl
"""
next_ = cast('ProtocolBase', # type: ignore[redundant-cast]
self._import_next_layer(proto, length, packet=packet, version=version,
payload=payload)) # type: ignore[arg-type,misc,call-arg]
payload=payload)) # type: ignore[misc,call-arg]
info, chain = next_.info, next_.protochain

# make next layer protocol name
Expand Down
2 changes: 1 addition & 1 deletion pcapkit/protocols/internet/ipv6.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ def _decode_next_layer(self, ipv6: 'Data_IPv6', proto: 'Optional[int]' = None,
token = _frame_depth.set(depth + len(_protos))
try:
next_ = self._import_next_layer(proto, packet=packet, version=6, extension=True,
payload=payload) # type: ignore[misc,call-arg,arg-type]
payload=payload) # type: ignore[misc,call-arg]
finally:
_frame_depth.reset(token)
info = next_.info
Expand Down
2 changes: 1 addition & 1 deletion pcapkit/protocols/internet/ipv6_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ def __post_init__(self, file: 'Optional[IO[bytes] | bytes]' = None, length: 'Opt
self._extf = extension

# call super __post_init__
super().__post_init__(file, length, version=version, extension=extension, **kwargs) # type: ignore[arg-type]
super().__post_init__(file, length, version=version, extension=extension, **kwargs)

def __length_hint__(self) -> 'int':
"""Return an estimated length for the object.
Expand Down
Loading
Loading