Source code for pcapkit.vendor.tcp.flags

# -*- coding: utf-8 -*-
"""TCP Header Flags
======================

.. module:: pcapkit.vendor.tcp.flags

This module contains the vendor crawler for **TCP Header Flags**,
which is automatically generating :class:`pcapkit.const.tcp.flags.Flags`.

"""

import csv
import re
import sys
from typing import TYPE_CHECKING

from pcapkit.vendor.default import Vendor

if TYPE_CHECKING:
    from typing import Callable

__all__ = ['Flags']

#: TCP header flags' abbreviation.
DATA = {
    15: 'FIN',
    14: 'SYN',
    13: 'RST',
    12: 'PSH',
    11: 'ACK',
    10: 'URG',
    9: 'ECE',
    8: 'CWR',
}

#: Default constant template of enumeration registry from IANA CSV.
LINE = lambda NAME, DOCS, FLAG, ENUM, MODL: f'''\
# -*- coding: utf-8 -*-
# pylint: disable=line-too-long
"""{(name := DOCS.split(' [', maxsplit=1)[0])}
{'=' * (len(name) + 6)}

.. module:: {MODL.replace('vendor', 'const')}

This module contains the constant enumeration for **{name}**,
which is automatically generated from :class:`{MODL}.{NAME}`.

"""

from typing import TYPE_CHECKING

from aenum import IntFlag

from pcapkit.corekit.enum import EnumRegistry
from pcapkit.utilities.exceptions import EnumValueError

if TYPE_CHECKING:
    from typing import Any

__all__ = ['{NAME}']

class {NAME}(EnumRegistry, IntFlag):
    """[{NAME}] {DOCS}

    .. versionchanged:: 1.5.0

       A value outside the 16-bit field raises :exc:`ValueError`, where
       :mod:`aenum` composed a pseudo-member for any integer.

    """

    {ENUM}

    @classmethod
    def _validate_value(cls, value: 'Any') -> 'None':
        """Reject ``value`` unless it passes the range check :meth:`_missing_` makes.

        Overrides :meth:`~pcapkit.corekit.enum.EnumLookup._validate_value`
        with that check and the message it raises, so
        :meth:`~pcapkit.corekit.enum.EnumRegistry.register` refuses exactly the
        values :meth:`~pcapkit.corekit.enum.EnumLookup.get` refuses, in the
        same words (:issue:`1816`).

        Args:
            value: Value about to be looked up or minted.

        Raises:
            EnumValueError: If ``value`` fails that check. A
                :exc:`ValueError`, so an ``except ValueError`` caller is
                unaffected.

        """
        if not ({FLAG}):
            raise EnumValueError(f'{{value!r}} is not a valid {{cls.__name__}}')

    @classmethod
    def _missing_(cls, value: 'int') -> '{NAME}':
        """Lookup function used when value is not found.

        Args:
            value: Value to get enum item.

        """
        if not ({FLAG}):
            raise ValueError(f'{{value!r}} is not a valid {{cls.__name__}}')
        return super()._missing_(value)
'''.strip()  # type: Callable[[str, str, str, str, str], str]


[docs] class Flags(Vendor): """TCP Header Flags""" #: Value limit checker. The registry indexes *bit offsets* 4 through 15, #: but the members it generates are ``1 << offset``, so the value domain a #: lookup has to accept is the 16-bit field those bits live in -- a #: composite such as ``SYN | ACK`` is a legitimate value and only the #: composite path reaches :meth:`~pcapkit.const.tcp.flags.Flags._missing_`. FLAG = 'isinstance(value, int) and 0 <= value <= 0xFFFF' #: Link to registry. LINK = 'https://www.iana.org/assignments/tcp-parameters/tcp-header-flags.csv' def process(self, data: 'list[str]') -> 'list[str]': # type: ignore[override] """Process CSV data. Args: data: CSV data. Returns: Enumeration fields. """ reader = csv.reader(data) next(reader) # header enum = [] # type: list[str] for item in reader: dscp = item[1] rfcs = item[2] temp = [] # type: list[str] for rfc in self.split_references(rfcs): if re.match(r'\d+', rfc): continue if 'RFC' in rfc and re.match(r'\d+', rfc[3:]): # temp.append(f'[{rfc[:3]} {rfc[3:]}]') temp.append(f'[:rfc:`{rfc[3:]}`]') else: temp.append(f'[{rfc}]'.replace('_', ' ')) tmp1 = f" {''.join(temp)}" if rfcs else '' desc = self.wrap_comment(re.sub(r'\r*\n', ' ', f'{dscp}{tmp1}', flags=re.MULTILINE)) code = item[0] name = DATA.get(int(code), dscp.split(' (')[0]).replace('Reserved for future use', 'Reserved') renm = self.rename(name or 'Unassigned', code, original=dscp) pres = f"{renm} = 1 << {code}" sufs = f'#: {desc}' # if len(pres) > 74: # sufs = f"\n{' '*80}{sufs}" # enum.append(f'{pres.ljust(76)}{sufs}') enum.append(f'{sufs}\n {pres}') return enum def context(self, data: 'list[str]') -> 'str': """Generate constant context. Args: data: CSV data. Returns: Constant context. """ enum = self.process(data) ENUM = '\n\n '.join(map(lambda s: s.rstrip(), enum)).strip() return LINE(self.NAME, self.DOCS, self.FLAG, ENUM, self.__module__)
if __name__ == '__main__': sys.exit(Flags()) # type: ignore[arg-type]