diff --git a/pcapkit/corekit/fields/collections.py b/pcapkit/corekit/fields/collections.py index 0e9de0e3db..22226c7fef 100644 --- a/pcapkit/corekit/fields/collections.py +++ b/pcapkit/corekit/fields/collections.py @@ -96,6 +96,9 @@ def __init__(self, length: 'int | Callable[[dict[str, Any]], int]' = lambda _: - item_type: 'Optional[FieldBase]' = None, callback: 'Callable[[Self, dict[str, Any]], None]' = lambda *_: None) -> 'None': #self._name = '' + if not hasattr(self, '_name'): + self._name = f'<{type(self).__name__[:-5].lower()}>' + self._callback = callback self._item_type = item_type diff --git a/pcapkit/corekit/fields/misc.py b/pcapkit/corekit/fields/misc.py index e54528ad73..44abe038c1 100644 --- a/pcapkit/corekit/fields/misc.py +++ b/pcapkit/corekit/fields/misc.py @@ -307,7 +307,9 @@ def __init__(self, length: 'int | Callable[[dict[str, Any]], int]' = lambda _: - default: '_TP | NoValueType | bytes' = NO_VALUE, protocol: 'Optional[Type[_TP] | str]' = None, callback: 'Callable[[Self, dict[str, Any]], None]' = lambda *_: None) -> 'None': - #self._name = '' + if not hasattr(self, '_name'): + self._name = f'<{type(self).__name__[:-5].lower()}>' + self._default = default # type: ignore[assignment] # NOTE: Through the property rather than straight to ``_protocol``, so a @@ -652,7 +654,9 @@ def __init__(self, length: 'int | Callable[[dict[str, Any]], int]' = lambda _: - default: '_TS | NoValueType | bytes' = NO_VALUE, packet: 'Optional[dict[str, Any]]' = None, callback: 'Callable[[Self, dict[str, Any]], None]' = lambda *_: None) -> 'None': - #self._name = '' + if not hasattr(self, '_name'): + self._name = f'<{type(self).__name__[:-5].lower()}>' + self._callback = callback if packet is None: diff --git a/tests/corekit/test_fields_collections_default_name_1491_unit.py b/tests/corekit/test_fields_collections_default_name_1491_unit.py new file mode 100644 index 0000000000..dfd9b6e6ea --- /dev/null +++ b/tests/corekit/test_fields_collections_default_name_1491_unit.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +"""Unnamed collection, payload and schema fields have a default name. + +GitHub issue #1491: :meth:`ListField.__init__ +` set no ``_name``, so +``ListField().name`` raised :exc:`AttributeError` -- and so did every subclass +that reaches it through ``super().__init__``, :class:`OptionField` included, +whose ``unpack`` reads ``self.name``. :class:`PayloadField` and +:class:`SchemaField` had the same gap. Other fields default to +``<{class name less "Field", lower-cased}>``, as :class:`FieldBase` and +:class:`Field` do in :mod:`pcapkit.corekit.fields.field`. + +Classes are imported inside each test, after +:func:`~tests._support.reimport_once_per_class`. + +""" + +import unittest + +from tests._support import reimport_once_per_class + + +class TestListFieldDefaultName(unittest.TestCase): + """Pin the default name of unnamed collection fields.""" + + def setUp(self) -> None: + reimport_once_per_class(self) + + def test_list_field(self) -> None: + from pcapkit.corekit.fields.collections import ListField + + self.assertEqual(ListField().name, '') + self.assertEqual(repr(ListField()), '') + + def test_option_field_subclasses(self) -> None: + from pcapkit.corekit.fields.collections import OptionField + from pcapkit.protocols.schema.transport.sctp import ChunkListField, Parameter + + for cls, name in ((OptionField, '