Skip to content

Commit 6e39a8e

Browse files
committed
Update internal type hints to new Python 3.12 conventions
1 parent 638941c commit 6e39a8e

File tree

3 files changed

+24
-29
lines changed

3 files changed

+24
-29
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SBB is a one-way tool: It takes source data and builds it into a binary file. If
88

99
## Requirements
1010

11-
SBB requires Python 3.11 or higher.
11+
SBB requires Python 3.12 or higher.
1212

1313
## Examples
1414

sbb/__init__.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,27 @@
11
import inspect
22
import json
33
import tomllib
4-
from typing import TypeVar
54
from pathlib import Path
65
from .datatypes import Array, Block, _Primitive, DataType
76

87

9-
_T = TypeVar('_T', bound=Block)
10-
11-
12-
def build_json(path: Path | str, root_type: type[_T]) -> _T:
8+
def build_json(path: Path | str, root_type: type[Block]) -> Block:
139
'''Builds a JSON file into a Block of the provided type.'''
1410
with open(path) as f:
1511
data = json.loads(f.read())
1612
data['_root_path'] = Path(path).parent.absolute()
1713
return root_type(None, data)
1814

1915

20-
def build_toml(path: Path | str, root_type: type[_T]) -> _T:
16+
def build_toml(path: Path | str, root_type: type[Block]) -> Block:
2117
'''Builds a TOML file into a Block of the provided type.'''
2218
with open(path) as f:
2319
data = tomllib.loads(f.read())
2420
data['_root_path'] = Path(path).parent.absolute()
2521
return root_type(None, data)
2622

2723

28-
def build_dict(data: dict, root_type: type[_T]) -> _T:
24+
def build_dict(data: dict, root_type: type[Block]) -> Block:
2925
'''Builds a dict into a Block of the provided type.'''
3026
return root_type(None, data)
3127

sbb/datatypes.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from abc import ABC, abstractmethod
66
from collections.abc import Iterable
77
from types import UnionType
8-
from typing import Optional, Any, TypeVar, Generic, Type, Callable
8+
from typing import Optional, Any, Callable, Sequence, ClassVar
99
from pathlib import Path
1010
from .errors import DataMissingError, ValidationError, BuildError
1111

@@ -141,7 +141,7 @@ class _Primitive(DataType, int):
141141
'''The base class of primitive integer-based datatypes like `U8`, `U16`,
142142
and `U32`.'''
143143

144-
bit_size = 0
144+
bit_size: ClassVar[int]
145145
_data: int
146146

147147
def __new__(cls, _: Optional[Block], data: int):
@@ -178,7 +178,7 @@ def _validate(self) -> None:
178178
range_min = -int(math.pow(2, self.bit_size - 1))
179179
range_max = int(math.pow(2, self.bit_size) - 1)
180180
if self._data < range_min or self._data > range_max:
181-
raise ValidationError(f"Value outside of range, must be {range_min} to {range_max}")
181+
raise ValidationError(f"Value {self._data} outside of range, must be {range_min} to {range_max}")
182182

183183
def __repr__(self) -> str:
184184
return f'{self._get_data()} ({hex(self._get_data())})'
@@ -187,19 +187,19 @@ def __repr__(self) -> str:
187187
class U8(_Primitive):
188188
'''The datatype representing an 8-bit integer. If a signed value is
189189
provided, the two's compliment is generated.'''
190-
bit_size = 8
190+
bit_size: ClassVar[int] = 8
191191

192192

193193
class U16(_Primitive):
194194
'''The datatype representing a 16-bit integer. If a signed value is
195195
provided, the two's compliment is generated.'''
196-
bit_size = 16
196+
bit_size: ClassVar[int] = 16
197197

198198

199199
class U32(_Primitive):
200200
'''The datatype representing a 32-bit integer. If a signed value is
201201
provided, the two's compliment is generated.'''
202-
bit_size = 32
202+
bit_size: ClassVar[int] = 32
203203

204204

205205
class Bytes(DataType):
@@ -300,14 +300,13 @@ def __bool__(self) -> bool:
300300
return False
301301

302302

303-
class Align(Bytes, Generic[TypeVar('T', bound=_Primitive)]):
303+
class Align[T: _Primitive](Bytes):
304304
'''The data alignment datatype.
305305
306-
Properties of type `Align` cannot be set
307-
directly via setter method or data. Instead, an `Align` datatype will
308-
produce the amount of padding bytes needed for the data structure to reach
309-
the desired byte alignment. For example, `Align[U32]` will align the data
310-
to the next 4-byte boundary.'''
306+
Properties of type `Align` cannot be set directly via setter method or
307+
data. Instead, an `Align` datatype will produce the amount of padding bytes
308+
needed for the data structure to reach the desired byte alignment. For
309+
example, `Align[U32]` will align the data to the next 4-byte boundary.'''
311310

312311
def __init__(self, parent: Optional[Block], pad_amount: int) -> None:
313312
super().__init__(parent, bytes(pad_amount))
@@ -378,7 +377,7 @@ def _build(self, data: Optional[Any]) -> None:
378377
"dependencies in these properties of "
379378
f"{type(self).__name__}:\n{failed}"))
380379

381-
def _get_data(self) -> list[DataType]:
380+
def _get_data(self) -> Sequence[DataType]:
382381
data = []
383382
for name in inspect.get_annotations(type(self), eval_str=True):
384383
data.append(getattr(self, name))
@@ -445,13 +444,13 @@ class _BlockItem:
445444
owner: Block
446445
offset: Optional[int] = None
447446
name: str
448-
datatype: Type
449-
argtype: Optional[Type] = None
447+
datatype: type
448+
argtype: Optional[type] = None
450449
value: DataType
451450
setter: Optional[Callable]
452451
dependencies: list[_BlockItem]
453452

454-
def __init__(self, owner: Block, name: str, datatype: Type, value: Optional[DataType]) -> None:
453+
def __init__(self, owner: Block, name: str, datatype: type, value: Optional[DataType]) -> None:
455454
self.owner = owner
456455
self.name = name
457456
if value is not None:
@@ -502,8 +501,8 @@ def build(self, data: Optional[dict]) -> None:
502501
if not hasattr(self.argtype, 'static_size'):
503502
raise BuildError(f"Align argument must be a primitive type with a statically-known size")
504503
alignment = self.argtype.static_size()
505-
offset_mod = self.owner.offset_of(self.name) % alignment
506-
pad_needed = alignment - offset_mod if offset_mod != 0 else 0
504+
offset_mod = (self.owner.offset_of(self.name) - 1) % alignment + 1
505+
pad_needed = alignment - offset_mod
507506
value = Align(self.owner, pad_needed)
508507
else:
509508
raise DataMissingError(f"No setter or dict value found for {self.name}")
@@ -545,10 +544,10 @@ def _ensure_value_wrapped(self, value: Any) -> DataType:
545544
return self.datatype(self.owner, value)
546545

547546

548-
class Array(Block, list, Generic[TypeVar('T', bound=DataType)]):
547+
class Array[T: DataType](Block, list[T]):
549548
'''A raw array of items in the specified type.'''
550549

551-
def __init__(self, parent: Optional[Block], datatype: Optional[Type] = None, data: list = []) -> None:
550+
def __init__(self, parent: Optional[Block], datatype: Optional[type] = None, data: list = []) -> None:
552551
self.size = self._size
553552
self._data = data
554553
super().__init__(parent)
@@ -562,7 +561,7 @@ def __init__(self, parent: Optional[Block], datatype: Optional[Type] = None, dat
562561
e.add_note(prop_name)
563562
raise
564563

565-
def _get_data(self) -> list[DataType]:
564+
def _get_data(self) -> Sequence[DataType]:
566565
return self
567566

568567
@classmethod

0 commit comments

Comments
 (0)