Skip to content

Commit 7ea03dd

Browse files
committed
fix(corekit): give unnamed ListField, PayloadField and SchemaField a default name (#1491)
ListField.__init__, PayloadField.__init__ and SchemaField.__init__ set no _name, so .name on an unnamed instance -- and on OptionField and its subclasses -- raised AttributeError. Default it with the hasattr idiom FieldBase and Field already use, giving <list>, <option>, <chunklist>, <payload>, <schema>, and so on.
1 parent d9e12e8 commit 7ea03dd

3 files changed

Lines changed: 86 additions & 2 deletions

File tree

‎pcapkit/corekit/fields/collections.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ def __init__(self, length: 'int | Callable[[dict[str, Any]], int]' = lambda _: -
9696
item_type: 'Optional[FieldBase]' = None,
9797
callback: 'Callable[[Self, dict[str, Any]], None]' = lambda *_: None) -> 'None':
9898
#self._name = '<list>'
99+
if not hasattr(self, '_name'):
100+
self._name = f'<{type(self).__name__[:-5].lower()}>'
101+
99102
self._callback = callback
100103
self._item_type = item_type
101104

‎pcapkit/corekit/fields/misc.py‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,9 @@ def __init__(self, length: 'int | Callable[[dict[str, Any]], int]' = lambda _: -
307307
default: '_TP | NoValueType | bytes' = NO_VALUE,
308308
protocol: 'Optional[Type[_TP] | str]' = None,
309309
callback: 'Callable[[Self, dict[str, Any]], None]' = lambda *_: None) -> 'None':
310-
#self._name = '<payload>'
310+
if not hasattr(self, '_name'):
311+
self._name = f'<{type(self).__name__[:-5].lower()}>'
312+
311313
self._default = default # type: ignore[assignment]
312314

313315
# 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 _: -
652654
default: '_TS | NoValueType | bytes' = NO_VALUE,
653655
packet: 'Optional[dict[str, Any]]' = None,
654656
callback: 'Callable[[Self, dict[str, Any]], None]' = lambda *_: None) -> 'None':
655-
#self._name = '<schema>'
657+
if not hasattr(self, '_name'):
658+
self._name = f'<{type(self).__name__[:-5].lower()}>'
659+
656660
self._callback = callback
657661

658662
if packet is None:
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# -*- coding: utf-8 -*-
2+
"""Unnamed collection, payload and schema fields have a default name.
3+
4+
GitHub issue #1491: :meth:`ListField.__init__
5+
<pcapkit.corekit.fields.collections.ListField.__init__>` set no ``_name``, so
6+
``ListField().name`` raised :exc:`AttributeError` -- and so did every subclass
7+
that reaches it through ``super().__init__``, :class:`OptionField` included,
8+
whose ``unpack`` reads ``self.name``. :class:`PayloadField` and
9+
:class:`SchemaField` had the same gap. Other fields default to
10+
``<{class name less "Field", lower-cased}>``, as :class:`FieldBase` and
11+
:class:`Field` do in :mod:`pcapkit.corekit.fields.field`.
12+
13+
Classes are imported inside each test, after
14+
:func:`~tests._support.reimport_once_per_class`.
15+
16+
"""
17+
18+
import unittest
19+
20+
from tests._support import reimport_once_per_class
21+
22+
23+
class TestListFieldDefaultName(unittest.TestCase):
24+
"""Pin the default name of unnamed collection fields."""
25+
26+
def setUp(self) -> None:
27+
reimport_once_per_class(self)
28+
29+
def test_list_field(self) -> None:
30+
from pcapkit.corekit.fields.collections import ListField
31+
32+
self.assertEqual(ListField().name, '<list>')
33+
self.assertEqual(repr(ListField()), '<ListField>')
34+
35+
def test_option_field_subclasses(self) -> None:
36+
from pcapkit.corekit.fields.collections import OptionField
37+
from pcapkit.protocols.schema.transport.sctp import ChunkListField, Parameter
38+
39+
for cls, name in ((OptionField, '<option>'), (ChunkListField, '<chunklist>')):
40+
with self.subTest(cls=cls.__name__):
41+
field = cls(base_schema=Parameter, type_name='type',
42+
registry=Parameter.registry)
43+
self.assertEqual(field.name, name)
44+
45+
def test_copy_keeps_default_name(self) -> None:
46+
from pcapkit.corekit.fields.collections import ListField
47+
48+
self.assertEqual(ListField()({}).name, '<list>')
49+
50+
def test_schema_name_wins(self) -> None:
51+
from pcapkit.protocols.schema.transport.sctp import INITChunk
52+
53+
self.assertEqual(INITChunk.__fields__['parameters'].name, 'parameters')
54+
55+
56+
class TestMiscFieldDefaultName(unittest.TestCase):
57+
"""Pin the default name of unnamed :mod:`~pcapkit.corekit.fields.misc` fields."""
58+
59+
def setUp(self) -> None:
60+
reimport_once_per_class(self)
61+
62+
def test_payload_field(self) -> None:
63+
from pcapkit.corekit.fields.misc import PayloadField
64+
65+
self.assertEqual(PayloadField().name, '<payload>')
66+
self.assertEqual(PayloadField()({}).name, '<payload>')
67+
68+
def test_schema_field(self) -> None:
69+
from pcapkit.corekit.fields.misc import SchemaField
70+
from pcapkit.protocols.schema.transport.sctp import Parameter
71+
72+
self.assertEqual(SchemaField(schema=Parameter).name, '<schema>')
73+
self.assertEqual(SchemaField(schema=Parameter)({}).name, '<schema>')
74+
75+
76+
if __name__ == '__main__':
77+
unittest.main()

0 commit comments

Comments
 (0)