Skip to content

Commit 1319659

Browse files
committed
revised info/schema_final decorators
* added FinalisedState to allow more complex state check * added _finalised keyword arg to allow mid-state finalisation
1 parent 03b930d commit 1319659

File tree

3 files changed

+52
-20
lines changed

3 files changed

+52
-20
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@
223223
"PCAPIO",
224224
"PCAPNG",
225225
"pcaprec",
226+
"phkt",
226227
"pkey",
227228
"plen",
228229
"pocsp",

pcapkit/corekit/infoclass.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
1212
"""
1313
import collections.abc
14+
import enum
1415
import itertools
1516
from typing import TYPE_CHECKING, Generic, TypeVar, cast, final
1617

@@ -29,24 +30,40 @@
2930
ST = TypeVar('ST', bound='Type[Info]')
3031

3132

32-
def info_final(cls: 'ST') -> 'ST':
33+
class FinalisedState(enum.IntEnum):
34+
"""Finalised state."""
35+
36+
#: Not finalised.
37+
NONE = enum.auto()
38+
#: Base class.
39+
BASE = enum.auto()
40+
#: Finalised.
41+
FINAL = enum.auto()
42+
43+
44+
def info_final(cls: 'ST', *, _finalised: 'bool' = True) -> 'ST':
3345
"""Finalise info class.
3446
47+
This decorator function is used to generate necessary
48+
attributes and methods for the decorated :class:`Info`
49+
class. It can be useful to reduce runtime generation
50+
time as well as caching already generated attributes.
51+
3552
Args:
3653
cls: Info class.
54+
_finalised: Whether to make the info class as finalised.
3755
3856
Returns:
3957
Finalised info class.
4058
4159
Notes:
42-
This decorator function is used to generate necessary
43-
attributes and methods for the decorated :class:`Info`
44-
class. It can be useful to reduce runtime generation
45-
time as well as caching already generated attributes.
60+
The decorator should only be used on the *final*
61+
class, otherwise, any subclasses derived from a
62+
finalised info class will not be re-finalised.
4663
4764
:meta decorator:
4865
"""
49-
if cls.__finalised__:
66+
if cls.__finalised__ == FinalisedState.FINAL:
5067
warn(f'{cls.__name__}: info class has been finalised; now skipping',
5168
InfoWarning, stacklevel=stacklevel())
5269
return cls
@@ -115,7 +132,11 @@ def info_final(cls: 'ST') -> 'ST':
115132
cls.__init__ = ns['__create_fn__']() # type: ignore[misc]
116133
cls.__init__.__qualname__ = f'{cls.__name__}.__init__' # type: ignore[misc]
117134

118-
cls.__finalised__ = True
135+
if not _finalised:
136+
cls.__finalised__ = FinalisedState.BASE
137+
return cls
138+
139+
cls.__finalised__ = FinalisedState.FINAL
119140
return final(cls)
120141

121142

@@ -148,7 +169,7 @@ class Info(Mapping[str, VT], Generic[VT]):
148169
__builtin__: 'set[str]'
149170

150171
#: Flag for finalised class initialisation.
151-
__finalised__ = False
172+
__finalised__: 'FinalisedState' = FinalisedState.NONE
152173

153174
#: List of additional built-in names.
154175
__additional__: 'list[str]' = []
@@ -167,8 +188,8 @@ def __new__(cls, *args: 'VT', **kwargs: 'VT') -> 'Self': # pylint: disable=unus
167188
**kwargs: Arbitrary keyword arguments.
168189
169190
"""
170-
if not cls.__finalised__:
171-
cls = info_final(cls)
191+
if cls.__finalised__ == FinalisedState.NONE:
192+
cls = info_final(cls, _finalised=False)
172193
self = super().__new__(cls)
173194

174195
# NOTE: We define the ``__map__`` and ``__map_reverse__`` attributes

pcapkit/protocols/schema/schema.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import itertools
88
from typing import TYPE_CHECKING, Generic, TypeVar, cast, final
99

10+
from pcapkit.corekit.infoclass import FinalisedState
1011
from pcapkit.corekit.fields.collections import ListField, OptionField
1112
from pcapkit.corekit.fields.field import NoValue, _Field
1213
from pcapkit.corekit.fields.misc import ConditionalField, ForwardMatchField, PayloadField
@@ -28,24 +29,29 @@
2829
ST = TypeVar('ST', bound='Type[Schema]')
2930

3031

31-
def schema_final(cls: 'ST') -> 'ST':
32+
def schema_final(cls: 'ST', *, _finalised: 'bool' = True) -> 'ST':
3233
"""Finalise schema class.
3334
35+
This decorator function is used to generate necessary
36+
attributes and methods for the decorated :class:`Schema`
37+
class. It can be useful to reduce runtime generation
38+
time as well as caching already generated attributes.
39+
3440
Args:
3541
cls: Schema class.
42+
_finalised: Whether to make the schema class finalised.
3643
3744
Returns:
3845
Finalised schema class.
3946
4047
Notes:
41-
This decorator function is used to generate necessary
42-
attributes and methods for the decorated :class:`Schema`
43-
class. It can be useful to reduce runtime generation
44-
time as well as caching already generated attributes.
48+
The decorator should only be used on the *final*
49+
class, otherwise, any subclasses derived from a
50+
finalised schema class will not be re-finalised.
4551
4652
:meta decorator:
4753
"""
48-
if cls.__finalised__:
54+
if cls.__finalised__ == FinalisedState.FINAL:
4955
warn(f'{cls.__name__}: schema has been finalised; now skipping',
5056
SchemaWarning, stacklevel=stacklevel())
5157
return cls
@@ -134,7 +140,11 @@ def schema_final(cls: 'ST') -> 'ST':
134140
cls.__init__ = ns['__create_fn__']() # type: ignore[misc]
135141
cls.__init__.__qualname__ = f'{cls.__name__}.__init__' # type: ignore[misc]
136142

137-
cls.__finalised__ = True
143+
if not _finalised:
144+
cls.__finalised__ = FinalisedState.BASE
145+
return cls
146+
147+
cls.__finalised__ = FinalisedState.FINAL
138148
return final(cls)
139149

140150

@@ -158,7 +168,7 @@ class Schema(Mapping[str, VT], Generic[VT]):
158168
__updated__: 'bool'
159169

160170
#: Flag for finalised class initialisation.
161-
__finalised__ = False
171+
__finalised__: 'FinalisedState' = FinalisedState.NONE
162172

163173
#: Field name of the payload.
164174
__payload__: 'str' = 'payload'
@@ -179,8 +189,8 @@ def __new__(cls, *args: 'VT', **kwargs: 'VT') -> 'Self': # pylint: disable=unus
179189
**kwargs: Arbitrary keyword arguments.
180190
181191
"""
182-
if not cls.__finalised__:
183-
cls = schema_final(cls)
192+
if cls.__finalised__ == FinalisedState.NONE:
193+
cls = schema_final(cls, _finalised=False)
184194
self = super().__new__(cls)
185195

186196
# NOTE: We define the ``__map__`` and ``__map_reverse__`` attributes

0 commit comments

Comments
 (0)