From bbf6eef454e706a8a99c9c6d1fcc473f66cafbd5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 21:01:23 +0000 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.22 → v0.16.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.22...v0.16.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a52e15d..b68fe0b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,7 +42,7 @@ repos: args: ["--line-length=132"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.22 + rev: v0.16.0 hooks: - id: ruff args: ["--fix", "--exit-non-zero-on-fix"] From b68e57a9afdc6c2e8d25bc599c77e3f75e8503cd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 21:02:28 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/conf.py | 2 +- objutils/__init__.py | 18 +++---- objutils/ash.py | 2 +- objutils/binfile.py | 12 ++--- objutils/cosmac.py | 4 +- objutils/dwarf/__init__.py | 5 +- objutils/dwarf/attrparser.py | 2 +- objutils/dwarf/encoding.py | 14 +++--- objutils/dwarf/readers.py | 1 - objutils/dwarf/sm.py | 2 - objutils/dwarf/traverser.py | 34 ++++++------- objutils/elf/__init__.py | 4 +- objutils/elf/model.py | 4 +- objutils/emon52.py | 2 +- objutils/etek.py | 6 +-- objutils/fpc.py | 8 ++-- objutils/hexdump.py | 2 +- objutils/hexfile.py | 71 ++++++++++++---------------- objutils/ieee695.py | 24 ++++------ objutils/ihex.py | 12 ++--- objutils/image.py | 41 ++++++++-------- objutils/logger.py | 8 ++-- objutils/mostec.py | 10 ++-- objutils/oocdtxt.py | 6 +-- objutils/pecoff/__init__.py | 2 +- objutils/pecoff/defs.py | 30 ++++++------ objutils/pecoff/pdb/__init__.py | 4 +- objutils/rca.py | 10 ++-- objutils/registry.py | 4 +- objutils/scripts/oj_coff_extract.py | 2 +- objutils/scripts/oj_coff_import.py | 2 +- objutils/scripts/oj_coff_info.py | 2 +- objutils/scripts/oj_coff_syms.py | 2 +- objutils/scripts/oj_elf_arm_attrs.py | 2 +- objutils/scripts/oj_elf_extract.py | 2 +- objutils/scripts/oj_elf_syms.py | 13 ++--- objutils/section.py | 34 ++++++------- objutils/shf.py | 7 +-- objutils/sig.py | 6 +-- objutils/srec.py | 24 ++++------ objutils/tek.py | 10 ++-- objutils/tests/test_image.py | 1 - objutils/titxt.py | 2 +- 43 files changed, 200 insertions(+), 253 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 4540d9c..23201b7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,7 +17,7 @@ def get_version(): import re from pathlib import Path - VERSION = re.compile(r'version\s*=\s*"(?P\d+\.\d+(\.\d+)?)\s*"', re.M) + VERSION = re.compile(r'version\s*=\s*"(?P\d+\.\d+(\.\d+)?)\s*"', re.MULTILINE) try: cfg = Path(r"../pyproject.toml") diff --git a/objutils/__init__.py b/objutils/__init__.py index eb09d66..0a381ca 100644 --- a/objutils/__init__.py +++ b/objutils/__init__.py @@ -17,16 +17,16 @@ __all__ = [ "Image", - "Section", - "LazySection", "InvalidAddressError", - "registry", - "load", - "loads", + "LazySection", + "Section", "dump", "dumps", + "load", + "loads", "probe", "probes", + "registry", ] __copyright__ = """ @@ -76,9 +76,9 @@ import objutils.srec import objutils.tek import objutils.titxt -from objutils.image import Image, InvalidAddressError # noqa: F401 +from objutils.image import Image, InvalidAddressError from objutils.registry import registry -from objutils.section import Section, LazySection # noqa: F401 +from objutils.section import Section, LazySection # Optional developer-friendly console and tracebacks; disabled by default for library consumers. _ENABLE_RICH = os.getenv("OBJUTILS_RICH", "0").lower() in {"1", "true", "yes"} @@ -158,7 +158,7 @@ def loads(codec_name: str, data: str | bytes | bytearray, join: bool = True, **k return registry.get(codec_name).Reader().loads(data, join=join, **kws) -def probe(fp: BinaryIO, **kws: Any) -> Optional[str]: +def probe(fp: BinaryIO, **kws: Any) -> str | None: """Try to guess codec from file. Returns @@ -195,7 +195,7 @@ def probe(fp: BinaryIO, **kws: Any) -> Optional[str]: return None -def probes(data: str | bytes | bytearray, **kws: Any) -> Optional[str]: +def probes(data: str | bytes | bytearray, **kws: Any) -> str | None: """Try to guess codec from bytes. Returns diff --git a/objutils/ash.py b/objutils/ash.py index 0139abb..7dc2bd5 100644 --- a/objutils/ash.py +++ b/objutils/ash.py @@ -44,7 +44,7 @@ from functools import partial from typing import Any -import objutils.hexfile as hexfile +from objutils import hexfile from objutils.checksums import lrc STX = "\x02" diff --git a/objutils/binfile.py b/objutils/binfile.py index d5a5e04..584a428 100644 --- a/objutils/binfile.py +++ b/objutils/binfile.py @@ -26,9 +26,9 @@ import io import zipfile from contextlib import closing -from typing import Any, BinaryIO, Union +from typing import Any, BinaryIO -import objutils.hexfile as hexfile +from objutils import hexfile from objutils.image import Image from objutils.section import Section @@ -42,7 +42,7 @@ class NoContiniousError(Exception): class Reader(hexfile.Reader): - def load(self, fp: Union[str, BinaryIO], address: int = 0x0000, **kws: Any) -> Image: + def load(self, fp: str | BinaryIO, address: int = 0x0000, **kws: Any) -> Image: if isinstance(fp, str): fp = open(fp, "rb") data = fp.read() @@ -52,7 +52,7 @@ def load(self, fp: Union[str, BinaryIO], address: int = 0x0000, **kws: Any) -> I fp.close() return img - def loads(self, image: Union[str, bytes, bytearray], address: int = 0x0000, **kws: Any) -> Image: + def loads(self, image: str | bytes | bytearray, address: int = 0x0000, **kws: Any) -> Image: if isinstance(image, str): return self.load(io.BytesIO(bytes(image, "ascii")), address) else: @@ -112,12 +112,12 @@ def probe(self, fp: BinaryIO, **kws: Any) -> bool: except (AttributeError, io.UnsupportedOperation): pass - def load(self, fp: Union[str, BinaryIO], **kws: Any) -> Image: + def load(self, fp: str | BinaryIO, **kws: Any) -> Image: """Load from zip file.""" # TODO: Implementation return Image([], valid=True) - def loads(self, data: Union[str, bytes, bytearray], **kws: Any) -> Image: + def loads(self, data: str | bytes | bytearray, **kws: Any) -> Image: """Load from zip bytes.""" return self.load(io.BytesIO(data) if isinstance(data, (bytes, bytearray)) else io.BytesIO(data.encode())) diff --git a/objutils/cosmac.py b/objutils/cosmac.py index b3e4aba..83774da 100644 --- a/objutils/cosmac.py +++ b/objutils/cosmac.py @@ -40,7 +40,7 @@ from collections.abc import Sequence from typing import Any -import objutils.hexfile as hexfile +from objutils import hexfile # Record type identifiers DATA0 = 1 # !MAAAA DD @@ -80,7 +80,7 @@ def check_line(self, line: Any, format_type: int) -> None: Cosmac format has no checksums, validation is minimal. """ # Cosmac has no checksums - nothing to validate - return None + return def probe(self, fp: Any, **kws: Any) -> bool: """Check if file matches RCA Cosmac format. diff --git a/objutils/dwarf/__init__.py b/objutils/dwarf/__init__.py index a2df128..2b9f710 100644 --- a/objutils/dwarf/__init__.py +++ b/objutils/dwarf/__init__.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# ruff: noqa: E402 # Imports come after module docstring """DWARF 4 debug information parser and processor. @@ -266,7 +265,7 @@ class Attribute: encoding: constants.AttributeEncoding form: constants.AttributeForm - special_value: Optional[Any] = None + special_value: Any | None = None def __iter__(self): yield self.encoding @@ -300,7 +299,6 @@ class Readers: Used internally by DwarfReaders to organize parsers. """ - pass @dataclass @@ -531,7 +529,6 @@ def _fetch(self, abbr_offset, item): ) continue if attr.attrValue != 0 and attr.formValue != 0: - # try: self.abbrevations[key].attrs.append( Attribute( diff --git a/objutils/dwarf/attrparser.py b/objutils/dwarf/attrparser.py index a061396..f2e20ec 100644 --- a/objutils/dwarf/attrparser.py +++ b/objutils/dwarf/attrparser.py @@ -321,7 +321,7 @@ def _type_summary(self, offset: int) -> str: tag = t.get("tag", "") name = "" attrs = t.get("attrs", {}) if isinstance(t.get("attrs"), dict) else {} - if "name" in attrs and attrs["name"]: + if attrs.get("name"): try: name = str(attrs["name"]) except (UnicodeDecodeError, ValueError, TypeError): diff --git a/objutils/dwarf/encoding.py b/objutils/dwarf/encoding.py index e02ec65..931eb7a 100644 --- a/objutils/dwarf/encoding.py +++ b/objutils/dwarf/encoding.py @@ -63,13 +63,11 @@ class Endianess(IntEnum): class ULEBError(ConstructError): """Raised when ULEB128 parsing or building encounters an error.""" - pass class SLEBError(ConstructError): """Raised when SLEB128 parsing or building encounters an error.""" - pass @singleton @@ -96,7 +94,7 @@ class ULEB(Construct): """ def __init__(self, *args) -> None: - super(__class__, self).__init__() + super().__init__() def _parse(self, stream, context, path: str | None = None) -> int: """Parse a ULEB128-encoded value from stream. @@ -171,7 +169,7 @@ class SLEB(Construct): """ def __init__(self, *args) -> None: - super(__class__, self).__init__() + super().__init__() def _parse(self, stream, context, path: str | None = None) -> int: """Parse a SLEB128-encoded value from stream. @@ -235,7 +233,7 @@ class One(Construct): """ def __init__(self, *args) -> None: - super(__class__, self).__init__() + super().__init__() def _parse(self, stream, context, path: str | None = None) -> int: """Parse returns constant 1.""" @@ -266,7 +264,7 @@ class Block(Construct): MASK: str | None = None def __init__(self, *args) -> None: - super(__class__, self).__init__() + super().__init__() def _parse(self, stream, context, path: str | None = None) -> bytes: """Parse block data with fixed-size length prefix. @@ -410,7 +408,7 @@ def __init__(self, size: int, endianess: Endianess) -> None: Raises: ValueError: If size not in (1, 2, 4, 8). """ - super(__class__, self).__init__() + super().__init__() idx = 0 if endianess == Endianess.Little else 1 if size not in (1, 2, 4, 8): raise ValueError(f"Address size '{size}' not supported.") @@ -473,7 +471,7 @@ def __init__(self, image: bytes, endianess: Endianess) -> None: self.endianess = endianess self.ntype = (Int32ul, Int32ub)[0 if endianess == Endianess.Little else 1] self.stype = CString(encoding="utf8") - super(__class__, self).__init__() + super().__init__() def _parse(self, stream, context, path: str | None = None) -> str: """Parse string pointer and resolve to actual string. diff --git a/objutils/dwarf/readers.py b/objutils/dwarf/readers.py index fec6df7..f31dec2 100644 --- a/objutils/dwarf/readers.py +++ b/objutils/dwarf/readers.py @@ -68,7 +68,6 @@ class Readers: strp, line_strp: String pointer readers. """ - pass class DwarfReaders: diff --git a/objutils/dwarf/sm.py b/objutils/dwarf/sm.py index 9f14a11..1dbe3ff 100644 --- a/objutils/dwarf/sm.py +++ b/objutils/dwarf/sm.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# ruff: noqa: E402 # Imports come after module docstring (standard Python practice) __copyright__ = """ @@ -370,7 +369,6 @@ def stack_op(self, stack): Default implementation does nothing (no-op). Operations that modify the stack must override this. """ - pass def __str__(self): """Return textual representation of operation with parameters. diff --git a/objutils/dwarf/traverser.py b/objutils/dwarf/traverser.py index e099524..73133be 100644 --- a/objutils/dwarf/traverser.py +++ b/objutils/dwarf/traverser.py @@ -117,7 +117,7 @@ from dataclasses import dataclass, field from functools import lru_cache from itertools import groupby -from typing import Any, Optional, Union +from typing import Any from objutils import Image, Section, symbols from objutils.dwarf.constants import ( @@ -174,7 +174,7 @@ """ -def is_type_encoding(encoding: Union[int, AttributeEncoding]) -> bool: +def is_type_encoding(encoding: int | AttributeEncoding) -> bool: """Check if a DWARF tag represents a type definition. Args: @@ -359,11 +359,11 @@ class Variable: """ name: str - section: Optional[str] = field(default=None) - elf_location: Optional[int] = field(default=None) - dwarf_location: Optional[str] = field(default=None) + section: str | None = field(default=None) + elf_location: int | None = field(default=None) + dwarf_location: str | None = field(default=None) static: bool = field(default=False) - type_desc: Optional[DIE] = field(default=None) + type_desc: DIE | None = field(default=None) _allocated: bool = field(default=False, repr=False) @@ -400,7 +400,7 @@ class Variable: # 'data_member_location': b'#\x00', -def get_attribute(attrs: dict[str, DIEAttribute], key: str, default: Union[int, str]) -> Union[int, str]: +def get_attribute(attrs: dict[str, DIEAttribute], key: str, default: int | str) -> int | str: """Get attribute value from attributes_map with fallback default. Args: @@ -417,7 +417,7 @@ def get_attribute(attrs: dict[str, DIEAttribute], key: str, default: Union[int, size = get_attribute(die.attributes_map, "byte_size", 0) ``` """ - attr: Optional[DIEAttribute] = attrs.get(key) + attr: DIEAttribute | None = attrs.get(key) if attr is None: return default else: @@ -760,7 +760,7 @@ def _safe_expression(self, attr): return f"" @lru_cache(maxsize=64 * 1024) - def type_tree(self, obj: Union[int, model.DebugInformationEntry, DIEAttribute]) -> dict[str, Any] | CircularReference: + def type_tree(self, obj: int | model.DebugInformationEntry | DIEAttribute) -> dict[str, Any] | CircularReference: """Resolve and return complete type tree for a DIE, offset, or attribute. This method accepts multiple input types for convenience: @@ -810,7 +810,7 @@ def type_tree(self, obj: Union[int, model.DebugInformationEntry, DIEAttribute]) # Case 2: attribute object (expected to be DW_AT_type) if isinstance(obj, DIEAttribute): # Try to resolve relative ref forms to absolute offset using the parent DIE if available - parent: Optional[model.DebugInformationEntry] = getattr(obj, "entry", None) + parent: model.DebugInformationEntry | None = getattr(obj, "entry", None) off = self._resolve_type_offset(obj, parent) if off is None: return {"tag": "", "attrs": {}} @@ -879,7 +879,7 @@ def variable(self, obj: model.DebugInformationEntry) -> Variable: _allocated=section_name in self.allocated_sections, ) - def get_value(self, var: Variable) -> Optional[Any]: + def get_value(self, var: Variable) -> Any | None: """Extract variable value from ELF section image. This method resolves the variable's address and extracts its value based @@ -926,7 +926,7 @@ def get_value(self, var: Variable) -> Optional[Any]: # No image for variable, i.e. .bss section and the like. return None # 1) Resolve address - addr: Optional[int] = None + addr: int | None = None if isinstance(var.elf_location, int): addr = var.elf_location elif var.dwarf_location is not None: @@ -948,7 +948,7 @@ def get_value(self, var: Variable) -> Optional[Any]: return None # 2a) Helpers to work with both dict-based and DIE-based nodes - def _get_tag(node: Any) -> Optional[str]: + def _get_tag(node: Any) -> str | None: if node is None: return None if isinstance(node, dict): @@ -1010,7 +1010,7 @@ def _unwrap_qualifiers_until_array(node: Any) -> Any: return cur # 2d) Unwrap qualifiers all the way down to base_type - def unwrap_to_base(d: Optional[DIE | dict | CircularReference]) -> Optional[DIE]: + def unwrap_to_base(d: DIE | dict | CircularReference | None) -> DIE | None: # Accept DIE or dict-like from older structures seen = 0 current = d @@ -1101,7 +1101,7 @@ def _reshape(flat: list[Any], dims: list[int]) -> list[Any]: out.append(_reshape(chunk, dims[1:])) return out - def _element_size(elem: Any) -> Optional[int]: + def _element_size(elem: Any) -> int | None: t = _get_tag(elem) attrs = _get_attrs(elem) if t in {"base_type", "pointer_type"}: @@ -1249,8 +1249,8 @@ def _element_size(elem: Any) -> Optional[int]: def _resolve_type_offset( self, type_attr: DIEAttribute, - context_die: Optional[model.DebugInformationEntry], - ) -> Optional[int]: + context_die: model.DebugInformationEntry | None, + ) -> int | None: """Resolve DW_AT_type attribute value to absolute DIE offset. Handles CU-relative reference forms (DW_FORM_ref1/2/4/8/udata) by adding diff --git a/objutils/elf/__init__.py b/objutils/elf/__init__.py index 8d0c56a..a6a0cb5 100644 --- a/objutils/elf/__init__.py +++ b/objutils/elf/__init__.py @@ -325,7 +325,7 @@ def _parse(self, stream: typing.Any, context: typing.Any, path: str) -> None: Returns: None always. """ - return None + return def _build(self, obj: typing.Any, stream: typing.Any, context: typing.Any, path: str) -> None: """Build nothing to stream. @@ -336,7 +336,6 @@ def _build(self, obj: typing.Any, stream: typing.Any, context: typing.Any, path: context: Context object (not used). path: Path in parent construct. """ - pass def _sizeof(self, context: typing.Any, path: str) -> int: """Return size as 0. @@ -479,7 +478,6 @@ def elf32Addr(self) -> None: This method is reserved for future ELF32-specific address handling. """ - pass def setByteOrder(self) -> None: """Placeholder for byte order configuration. diff --git a/objutils/elf/model.py b/objutils/elf/model.py index 1bb4e5e..b6478b4 100644 --- a/objutils/elf/model.py +++ b/objutils/elf/model.py @@ -1182,7 +1182,6 @@ class DebugInformation(Base, RidMixIn): as a placeholder for future expansion of debug metadata storage. """ - pass class CompilationUnit(Base, RidMixIn): @@ -1193,7 +1192,6 @@ class CompilationUnit(Base, RidMixIn): Currently serves as a placeholder for future expansion of CU-level metadata. """ - pass def calculateCacheSize(value: int) -> int: @@ -1214,7 +1212,7 @@ def calculateCacheSize(value: int) -> int: REGEX_CACHE: dict[str, Any] = {} -def regexer(value: Optional[str], expr: Optional[str]) -> int: +def regexer(value: str | None, expr: str | None) -> int: """SQLite-compatible regex function for pattern matching. Used with SQLite's CREATE_FUNCTION to enable REGEXP operators in SQL queries. diff --git a/objutils/emon52.py b/objutils/emon52.py index d407f36..5d23d97 100644 --- a/objutils/emon52.py +++ b/objutils/emon52.py @@ -37,7 +37,7 @@ from collections.abc import Sequence from typing import Any, BinaryIO -import objutils.hexfile as hexfile +from objutils import hexfile # Record type identifiers (Intel HEX compatible) DATA = 0 diff --git a/objutils/etek.py b/objutils/etek.py index 43f178a..4f21ed7 100644 --- a/objutils/etek.py +++ b/objutils/etek.py @@ -46,9 +46,9 @@ from collections.abc import Sequence from typing import Any -import objutils.checksums as checksums -import objutils.hexfile as hexfile -import objutils.utils as utils +from objutils import checksums +from objutils import hexfile +from objutils import utils # Record type identifiers DATA = 1 diff --git a/objutils/fpc.py b/objutils/fpc.py index cd164ac..40e620c 100644 --- a/objutils/fpc.py +++ b/objutils/fpc.py @@ -40,8 +40,8 @@ from functools import partial from typing import Any, BinaryIO -import objutils.hexfile as hexfile -import objutils.utils as utils +from objutils import hexfile +from objutils import utils from objutils import checksums from objutils.image import Image from objutils.utils import create_string_buffer, slicer @@ -55,7 +55,7 @@ MAPPING = dict(enumerate(chr(n) for n in range(37, 123) if n not in (42,))) REV_MAPPING = {ord(value): key for key, value in MAPPING.items()} -NULLS = re.compile(r"\0*\s*!M\s*(.*)", re.DOTALL | re.M) +NULLS = re.compile(r"\0*\s*!M\s*(.*)", re.DOTALL | re.MULTILINE) VALID_CHARS = re.compile(r"^\{}[{}]+$".format(PREFIX, re.escape("".join(MAPPING.values())))) atoi16 = partial(int, base=16) @@ -91,7 +91,7 @@ def decode(self, fp: BinaryIO) -> str: """ self.last_address = 0 out_lines = [] - for line in fp.readlines(): + for line in fp: line = line.strip() startSym, line = line[0], line[1:] diff --git a/objutils/hexdump.py b/objutils/hexdump.py index c4a45af..903f159 100644 --- a/objutils/hexdump.py +++ b/objutils/hexdump.py @@ -1,8 +1,8 @@ #!/usr/bin/env python __all__ = [ - "Dumper", "CanonicalDumper", + "Dumper", "isprintable", ] diff --git a/objutils/hexfile.py b/objutils/hexfile.py index a42c566..0a194f9 100644 --- a/objutils/hexfile.py +++ b/objutils/hexfile.py @@ -276,7 +276,7 @@ def calculate_checksum(self, record): from functools import partial from operator import itemgetter from pathlib import Path -from typing import Any, BinaryIO, Optional, Protocol, Union +from typing import Any, BinaryIO, Protocol from objutils.image import Image from objutils.logger import Logger @@ -334,37 +334,31 @@ def calculate_checksum(self, record): class HexFileError(Exception): """Base exception for all hex file operations.""" - pass class ParseError(HexFileError): """Base for parsing-related errors.""" - pass class InvalidRecordTypeError(ParseError): """Raised when record type is not recognized.""" - pass class InvalidRecordLengthError(ParseError): """Raised when record length doesn't match data.""" - pass class InvalidRecordChecksumError(ParseError): """Raised when checksum validation fails.""" - pass class AddressRangeToLargeError(HexFileError): """Raised when address exceeds format capabilities.""" - pass # Deprecated alias for backward compatibility @@ -412,8 +406,8 @@ class MetaRecord: """Metadata record (header/footer information).""" format_type: int - address: Optional[int] - chunk: Optional[bytearray] + address: int | None + chunk: bytearray | None # ============================================================================ @@ -437,7 +431,7 @@ class FormatParser: Example: "S0LLAAAADDCC" describes Motorola S0 record format. """ - def __init__(self, fmt: str, data_separator: Optional[str] = None): + def __init__(self, fmt: str, data_separator: str | None = None): self.fmt = fmt self.translated_format: list[tuple[int, int, str]] = [] self.data_separator = data_separator @@ -504,13 +498,13 @@ class Container: """Modern attribute container for parsed hex records.""" line_number: int = 0 - address: Optional[int] = None - length: Optional[int] = None - type: Optional[int] = None - checksum: Optional[int] = None - addrChecksum: Optional[int] = None - chunk: Optional[bytearray] = None - junk: Optional[str] = None + address: int | None = None + length: int | None = None + type: int | None = None + checksum: int | None = None + addrChecksum: int | None = None + chunk: bytearray | None = None + junk: str | None = None processing_instructions: list[Any] = field(default_factory=list) def add_processing_instruction(self, pi: Any) -> None: @@ -536,7 +530,7 @@ def error(self, msg: str) -> None: def warn(self, msg: str) -> None: """Log warning.""" - self.logger.warn(msg) + self.logger.warning(msg) def info(self, msg: str) -> None: """Log info message.""" @@ -555,15 +549,15 @@ class ReaderProtocol(Protocol): valid: bool formats: list[tuple[int, re.Pattern]] - def load(self, fp: Union[str, Path, BinaryIO], join: bool = False, **kws: Any) -> Image: ... + def load(self, fp: str | Path | BinaryIO, join: bool = False, **kws: Any) -> Image: ... - def loads(self, image: Union[str, bytes, bytearray], join: bool = False, **kws: Any) -> Image: ... + def loads(self, image: str | bytes | bytearray, join: bool = False, **kws: Any) -> Image: ... def read(self, fp: BinaryIO, join: bool = False) -> Image: ... def probe(self, fp: BinaryIO, **kws: Any) -> bool: ... - def probes(self, image: Union[str, bytes, bytearray]) -> bool: ... + def probes(self, image: str | bytes | bytearray) -> bool: ... def check_line(self, line: Any, format_type: int) -> None: ... @@ -582,7 +576,7 @@ class WriterProtocol(Protocol): logger: Logger valid: bool - def dump(self, fp: Union[str, Path, BinaryIO], image: Image, row_length: int = 16, **kws: Any) -> None: ... + def dump(self, fp: str | Path | BinaryIO, image: Image, row_length: int = 16, **kws: Any) -> None: ... def dumps(self, image: Image, row_length: int = 16, **kws: Any) -> str: ... @@ -596,9 +590,9 @@ def set_parameters(self, **kws: Any) -> None: ... def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: ... - def compose_header(self, meta: Mapping[str, Any]) -> Optional[str]: ... + def compose_header(self, meta: Mapping[str, Any]) -> str | None: ... - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: ... + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: ... # ============================================================================ @@ -781,9 +775,9 @@ def is_data_line(self, line, format_type): # Class attributes (override in subclasses) ALIGNMENT: int = 0 # 2**n (fixed typo: was ALIGMENT) ALIGMENT: int = 0 # Deprecated alias for backward compatibility - DATA_SEPARATOR: Optional[str] = None + DATA_SEPARATOR: str | None = None VALID_CHARS: re.Pattern[str] = re.compile(r"^[a-fA-F0-9 :/;,%\n\r!?S]*$") - FORMAT_SPEC: Union[str, list[tuple[int, str]], None] = None + FORMAT_SPEC: str | list[tuple[int, str]] | None = None def __init__(self) -> None: """Initialize reader with format specification.""" @@ -801,7 +795,7 @@ def __init__(self) -> None: pattern = FormatParser(format_str, self.DATA_SEPARATOR).parse() self.formats.append((format_type, pattern)) - def load(self, fp: Union[str, Path, BinaryIO], join: bool = False, **kws: Any) -> Image: + def load(self, fp: str | Path | BinaryIO, join: bool = False, **kws: Any) -> Image: """Load image from file path or file-like object. Args: @@ -821,7 +815,7 @@ def load(self, fp: Union[str, Path, BinaryIO], join: bool = False, **kws: Any) - fp.close() return data - def loads(self, image: Union[str, bytes, bytearray], join: bool = False, **kws: Any) -> Image: + def loads(self, image: str | bytes | bytearray, join: bool = False, **kws: Any) -> Image: """Load image from string or bytes. Args: @@ -838,7 +832,7 @@ def loads(self, image: Union[str, bytes, bytearray], join: bool = False, **kws: buffer = create_string_buffer(image) return self.load(buffer, join=join) - def _parse_optional_int(self, groups: dict[str, Optional[str]], key: str) -> Optional[int]: + def _parse_optional_int(self, groups: dict[str, str | None], key: str) -> int | None: """Parse an optional numeric capture group using ``atoi``. Args: @@ -882,7 +876,7 @@ def read(self, fp: BinaryIO, join: bool = False) -> Image: continue matched = True - dict_: dict[str, Optional[str]] = match.groupdict() + dict_: dict[str, str | None] = match.groupdict() if not dict_: continue @@ -897,7 +891,7 @@ def read(self, fp: BinaryIO, join: bool = False) -> Image: junk = dict_.get("junk") # Parse data chunk - chunk: Optional[bytearray] = None + chunk: bytearray | None = None chunk_str = dict_.get("chunk") if chunk_str is not None: if self.DATA_SEPARATOR: @@ -1036,7 +1030,7 @@ def probe(self, fp: BinaryIO, **kws: Any) -> bool: return match_ratio >= MIN_MATCH_THRESHOLD - def probes(self, image: Union[str, bytes, bytearray], **kws: Any) -> bool: + def probes(self, image: str | bytes | bytearray, **kws: Any) -> bool: """Test if string/bytes matches this format. Args: @@ -1100,7 +1094,6 @@ def special_processing(self, line: Any, format_type: int) -> None: line: Parsed line container format_type: Format type identifier """ - pass def parseData(self, line: Any, format_type: int) -> bool: """Parse data from line (optional override). @@ -1345,7 +1338,7 @@ def __init__(self) -> None: self.logger = Logger("Writer") self.row_length = 16 - def dump(self, fp: Union[str, Path, BinaryIO], image: Image, row_length: int = 16, **kws: Any) -> None: + def dump(self, fp: str | Path | BinaryIO, image: Image, row_length: int = 16, **kws: Any) -> None: """Write image to file. Args: @@ -1448,7 +1441,6 @@ def pre_processing(self, image: Image) -> None: Args: image: Image to process """ - pass def set_parameters(self, **kws: Any) -> None: """Set writer parameters from keywords. @@ -1484,7 +1476,7 @@ def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: """ raise NotImplementedError("Subclasses must implement compose_row()") - def compose_header(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_header(self, meta: Mapping[str, Any]) -> str | None: """Compose file header (optional override). Args: @@ -1495,7 +1487,7 @@ def compose_header(self, meta: Mapping[str, Any]) -> Optional[str]: """ return None - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose file footer (optional override). Args: @@ -1654,7 +1646,6 @@ def read(self, fp: BinaryIO, join: bool = False) -> Image: def check_line(self, line: Any, format_type: int) -> None: """No validation needed for ASCII hex formats.""" - pass def is_data_line(self, line: Any, format_type: int) -> bool: """All lines with data pattern are data lines.""" @@ -1688,12 +1679,12 @@ def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: """ return self.separators.join([f"{b:02X}" for b in row]) - def compose_header(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_header(self, meta: Mapping[str, Any]) -> str | None: """Compose header with start address.""" if "start_address" in meta: return f"@{meta['start_address']:04X}" return None - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose footer (q marker for TI-TXT).""" return "q" diff --git a/objutils/ieee695.py b/objutils/ieee695.py index cd72f94..b29b184 100644 --- a/objutils/ieee695.py +++ b/objutils/ieee695.py @@ -635,24 +635,24 @@ def onF1(self): localGlobal = self.checkOptional(self.fpos) # noqa: F841 elif attrDef == 37: self.info.objectFormatVersionNumber = self.readNumber(self.fpos) - self.info.objectFormatRevisionLevel = self.readNumber(self.fpos) # noqa: F841 - elif attrDef == 38: # noqa: F841 - self.info.objectFormatType = ObjectFormatTypes[self.readNumber(self.fpos)] # noqa: F841 - elif attrDef == 39: # noqa: F841 + self.info.objectFormatRevisionLevel = self.readNumber(self.fpos) + elif attrDef == 38: + self.info.objectFormatType = ObjectFormatTypes[self.readNumber(self.fpos)] + elif attrDef == 39: self.info.symbolCaseSensitivity = CaseSensitivity[self.readNumber(self.fpos)] elif attrDef == 40: self.info.memoryModel = MemoryModel[self.readNumber(self.fpos)] - elif attrDef == 50: # noqa: F841 + elif attrDef == 50: year = self.readNumber(self.fpos) month = self.readNumber(self.fpos) day = self.readNumber(self.fpos) - hour = self.readNumber(self.fpos) # noqa: F841 - minute = self.readNumber(self.fpos) # noqa: F841 + hour = self.readNumber(self.fpos) + minute = self.readNumber(self.fpos) second = self.readNumber(self.fpos) self.info.creationDate = datetime(year, month, day, hour, minute, second) elif attrDef == 51: self.info.commandLine = self.readString(self.fpos) - elif attrDef == 52: # noqa: F841 + elif attrDef == 52: self.info.executionStatus = self.readNumber(self.fpos) elif attrDef == 53: self.info.hostEnvironment = self.readNumber(self.fpos) @@ -683,15 +683,11 @@ def onST(self): if f == "A": s, t = self.readCharacter(self.fpos), self.readCharacter(self.fpos) sectionType = f + s + t - elif f == "B": - pass - elif f == "C": + elif f == "B" or f == "C": pass elif f == "E": pass # todo: 'EA' / 'EZ' !!! - elif f == "M": - pass - elif f == "T": + elif f == "M" or f == "T": pass # todo: 'ZC' / 'ZM'. else: diff --git a/objutils/ihex.py b/objutils/ihex.py index 65656bd..b0e5d93 100644 --- a/objutils/ihex.py +++ b/objutils/ihex.py @@ -27,11 +27,11 @@ from collections.abc import Mapping, Sequence from functools import partial -from typing import Any, Optional +from typing import Any -import objutils.checksums as checksums -import objutils.hexfile as hexfile -import objutils.utils as utils +from objutils import checksums +from objutils import hexfile +from objutils import utils from objutils.checksums import COMPLEMENT_TWOS, lrc # Record type identifiers @@ -168,7 +168,7 @@ def pre_processing(self, image: hexfile.Image) -> None: Args: image: Image object to write """ - self.previosAddress: Optional[int] = None # Note: typo kept for compatibility + self.previosAddress: int | None = None # Note: typo kept for compatibility self.start_address: int = 0 def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: @@ -214,7 +214,7 @@ def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: result += f":{length:02X}{address:04X}{DATA:02X}{Writer.hex_bytes(row)}{checksum:02X}" return result - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose EOF record. Args: diff --git a/objutils/image.py b/objutils/image.py index 83c8642..f0b3a72 100644 --- a/objutils/image.py +++ b/objutils/image.py @@ -277,7 +277,7 @@ from bisect import bisect_right from collections.abc import Iterable from operator import attrgetter, eq -from typing import Any, Optional, Protocol, Union +from typing import Any, Protocol from objutils.exceptions import InvalidAddressError from objutils.section import Section, join_sections @@ -413,9 +413,9 @@ class Image: def __init__( self, - sections: Optional[Union[Section, Iterable[Section]]] = None, + sections: Section | Iterable[Section] | None = None, join: bool = True, - meta: Optional[dict[str, Any]] = None, + meta: dict[str, Any] | None = None, ) -> None: if meta is None: meta = {} @@ -507,8 +507,7 @@ def contains_range(self, addr: int, size: int) -> bool: from operator import attrgetter idx = bisect.bisect_right(self.sections, current_addr, key=attrgetter("start_address")) - 1 - if idx < 0: - idx = 0 + idx = max(idx, 0) while idx < len(self.sections) and remaining_size > 0: section = self.sections[idx] @@ -581,7 +580,7 @@ def read(self, addr: int, length: int, **kws: Any) -> bytes: """ return self._call_address_function("read", addr, length, **kws) - def write(self, addr: int, data: Union[bytes, bytearray], **kws: Any) -> None: + def write(self, addr: int, data: bytes | bytearray, **kws: Any) -> None: """Write bytes to image. Args: @@ -594,7 +593,7 @@ def write(self, addr: int, data: Union[bytes, bytearray], **kws: Any) -> None: """ self._call_address_function("write", addr, data, **kws) - def read_numeric(self, addr: int, dtype: str, **kws: Any) -> Union[int, float]: + def read_numeric(self, addr: int, dtype: str, **kws: Any) -> int | float: """Read a single numeric value with explicit endianness. Reads an integer or floating-point value from the specified address, @@ -640,7 +639,7 @@ def read_numeric(self, addr: int, dtype: str, **kws: Any) -> Union[int, float]: """ return self._call_address_function("read_numeric", addr, dtype, **kws) - def write_numeric(self, addr: int, value: Union[int, float], dtype: str, **kws: Any) -> None: + def write_numeric(self, addr: int, value: float, dtype: str, **kws: Any) -> None: """Write a single numeric value with explicit endianness. Writes an integer or floating-point value to the specified address, @@ -675,14 +674,14 @@ def write_numeric(self, addr: int, value: Union[int, float], dtype: str, **kws: """ self._call_address_function("write_numeric", addr, value, dtype, **kws) - def read_asam_numeric(self, addr: int, dtype: str, byte_order: str = "MSB_LAST", **kws: Any) -> Union[int, float]: + def read_asam_numeric(self, addr: int, dtype: str, byte_order: str = "MSB_LAST", **kws: Any) -> int | float: """Read a numeric ASAM datatype with ECU byte order semantics.""" return self._call_address_function("read_asam_numeric", addr, dtype, byte_order, **kws) def write_asam_numeric( self, addr: int, - value: Union[int, float], + value: float, dtype: str, byte_order: str = "MSB_LAST", **kws: Any, @@ -705,14 +704,14 @@ def read_asam_numeric_array( dtype: str, byte_order: str = "MSB_LAST", **kws: Any, - ) -> list[Union[int, float]]: + ) -> list[int | float]: """Read an ASAM numeric array with ECU byte order semantics.""" return self._call_address_function("read_asam_numeric_array", addr, length, dtype, byte_order, **kws) def write_asam_numeric_array( self, addr: int, - data: Iterable[Union[int, float]], + data: Iterable[int | float], dtype: str, byte_order: str = "MSB_LAST", **kws: Any, @@ -720,7 +719,7 @@ def write_asam_numeric_array( """Write an ASAM numeric array with ECU byte order semantics.""" self._call_address_function("write_asam_numeric_array", addr, data, dtype, byte_order, **kws) - def read_numeric_array(self, addr: int, length: int, dtype: str, **kws: Any) -> list[Union[int, float]]: + def read_numeric_array(self, addr: int, length: int, dtype: str, **kws: Any) -> list[int | float]: """Read array of numeric values from image. Args: @@ -737,7 +736,7 @@ def read_numeric_array(self, addr: int, length: int, dtype: str, **kws: Any) -> """ return self._call_address_function("read_numeric_array", addr, length, dtype, **kws) - def write_numeric_array(self, addr: int, data: Iterable[Union[int, float]], dtype: str, **kws: Any) -> None: + def write_numeric_array(self, addr: int, data: Iterable[int | float], dtype: str, **kws: Any) -> None: """Write array of numeric values to image. Args: @@ -754,7 +753,7 @@ def write_numeric_array(self, addr: int, data: Iterable[Union[int, float]], dtyp """ self._call_address_function("write_numeric_array", addr, data, dtype, **kws) - def write_ndarray(self, addr: int, array: Any, order: Optional[str] = None, **kws: Any) -> None: + def write_ndarray(self, addr: int, array: Any, order: str | None = None, **kws: Any) -> None: """Write NumPy ndarray to image. Args: @@ -794,8 +793,8 @@ def read_ndarray( addr: int, length: int, dtype: str, - shape: Optional[tuple[int, ...]] = None, - order: Optional[str] = None, + shape: tuple[int, ...] | None = None, + order: str | None = None, **kws: Any, ) -> Any: """Read NumPy ndarray from image. @@ -821,7 +820,7 @@ def read_asam_ndarray( addr: int, length: int, dtype: str, - shape: Optional[tuple[int, ...]] = None, + shape: tuple[int, ...] | None = None, byte_order: str = "MSB_LAST", index_mode: str = "ROW_DIR", **kws: Any, @@ -893,7 +892,7 @@ def _address_contained(self, address: int, length: int) -> bool: return any(address < (s.start_address + len(s)) and s.start_address < end for s in self.sections) def insert_section( - self, data: Union[bytes, bytearray, memoryview, str], start_address: Optional[int] = None, join: bool = True + self, data: bytes | bytearray | memoryview | str, start_address: int | None = None, join: bool = True ) -> None: """Insert/add a new section to image. @@ -949,7 +948,7 @@ def get_section(self, address: int) -> Section: result = bisect_right(self._sections, address, key=attrgetter("start_address")) return self._sections[result - 1] - def update_section(self, data: Union[bytes, bytearray], address: Optional[int] = None) -> None: + def update_section(self, data: bytes | bytearray, address: int | None = None) -> None: """Update existing section data. Args: @@ -969,7 +968,7 @@ def update_section(self, data: Union[bytes, bytearray], address: Optional[int] = if not self._address_contained(address, len(data)): raise InvalidAddressError("Address-space not in range") - def delete_section(self, address: Optional[int] = None) -> None: + def delete_section(self, address: int | None = None) -> None: """Delete section containing the specified address. Args: diff --git a/objutils/logger.py b/objutils/logger.py index a51c631..077ca99 100644 --- a/objutils/logger.py +++ b/objutils/logger.py @@ -30,7 +30,7 @@ class Logger: LOGGER_BASE_NAME = "objutils" FORMAT = "[%(levelname)s (%(name)s)]: %(message)s" - def __init__(self, name, level=logging.WARN): + def __init__(self, name, level=logging.WARNING): self.logger = logging.getLogger(f"{self.LOGGER_BASE_NAME}.{name}") self.logger.setLevel(level) handler = logging.StreamHandler() @@ -55,7 +55,7 @@ def info(self, message): self.log(message, logging.INFO) def warn(self, message): - self.log(message, logging.WARN) + self.log(message, logging.WARNING) def debug(self, message): self.log(message, logging.DEBUG) @@ -75,11 +75,11 @@ def silent(self): def setLevel(self, level): LEVEL_MAP = { "INFO": logging.INFO, - "WARN": logging.WARN, + "WARN": logging.WARNING, "DEBUG": logging.DEBUG, "ERROR": logging.ERROR, "CRITICAL": logging.CRITICAL, } if isinstance(level, str): - level = LEVEL_MAP.get(level.upper(), logging.WARN) + level = LEVEL_MAP.get(level.upper(), logging.WARNING) self.logger.setLevel(level) diff --git a/objutils/mostec.py b/objutils/mostec.py index 59efbaa..a5888fe 100644 --- a/objutils/mostec.py +++ b/objutils/mostec.py @@ -37,12 +37,12 @@ """ from collections.abc import Mapping, Sequence -from typing import Any, Optional +from typing import Any import io -import objutils.checksums as checksums -import objutils.hexfile as hexfile -import objutils.utils as utils +from objutils import checksums +from objutils import hexfile +from objutils import utils # Record type identifiers DATA = 1 @@ -186,7 +186,7 @@ def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: line = f";{length:02X}{address:04X}{Writer.hex_bytes(row)}{checksum:04X}" return line - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose EOF record. Args: diff --git a/objutils/oocdtxt.py b/objutils/oocdtxt.py index 6ea2e02..9c8ccea 100644 --- a/objutils/oocdtxt.py +++ b/objutils/oocdtxt.py @@ -48,9 +48,9 @@ import re from collections.abc import Sequence from pathlib import Path -from typing import Any, BinaryIO, Union +from typing import Any, BinaryIO -import objutils.hexfile as hexfile +from objutils import hexfile from objutils.image import Image from objutils.section import Section, join_sections @@ -183,7 +183,7 @@ class Writer(hexfile.Writer): MAX_ADDRESS_BITS = 32 - def dump(self, fp: Union[str, Path, BinaryIO], image: Image, row_length: int = DEFAULT_ROW_LENGTH, **kws: Any) -> None: + def dump(self, fp: str | Path | BinaryIO, image: Image, row_length: int = DEFAULT_ROW_LENGTH, **kws: Any) -> None: """Write *image* to *fp*. Args: diff --git a/objutils/pecoff/__init__.py b/objutils/pecoff/__init__.py index a7c73a6..21496c5 100644 --- a/objutils/pecoff/__init__.py +++ b/objutils/pecoff/__init__.py @@ -577,7 +577,7 @@ def create_image( add_image_base: bool = True, include_pattern: str = "", exclude_pattern: str = "", - callback: typing.Optional[typing.Callable[[Section], None]] = None, + callback: typing.Callable[[Section], None] | None = None, ) -> Image: """Build objutils.Image from PE sections. diff --git a/objutils/pecoff/defs.py b/objutils/pecoff/defs.py index 268606b..734175d 100644 --- a/objutils/pecoff/defs.py +++ b/objutils/pecoff/defs.py @@ -232,10 +232,16 @@ def decode_characteristics(characteristics: int) -> list[str]: __all__ = [ - "PE_SIGNATURE", - "OPTIONAL_HDR_MAGIC_PE32", - "OPTIONAL_HDR_MAGIC_PE32_PLUS", - "IMAGE_FILE_MACHINE_UNKNOWN", + "IMAGE_FILE_32BIT_MACHINE", + "IMAGE_FILE_AGGRESSIVE_WS_TRIM", + "IMAGE_FILE_BYTES_REVERSED_HI", + "IMAGE_FILE_BYTES_REVERSED_LO", + "IMAGE_FILE_DEBUG_STRIPPED", + "IMAGE_FILE_DLL", + "IMAGE_FILE_EXECUTABLE_IMAGE", + "IMAGE_FILE_LARGE_ADDRESS_AWARE", + "IMAGE_FILE_LINE_NUMS_STRIPPED", + "IMAGE_FILE_LOCAL_SYMS_STRIPPED", "IMAGE_FILE_MACHINE_ALPHA", "IMAGE_FILE_MACHINE_ALPHA64", "IMAGE_FILE_MACHINE_AM33", @@ -253,21 +259,12 @@ def decode_characteristics(characteristics: int) -> list[str]: "IMAGE_FILE_MACHINE_LOONGARCH64", "IMAGE_FILE_MACHINE_M32R", "IMAGE_FILE_MACHINE_MIPS16", + "IMAGE_FILE_MACHINE_UNKNOWN", + "IMAGE_FILE_NET_RUN_FROM_SWAP", "IMAGE_FILE_RELOCS_STRIPPED", - "IMAGE_FILE_EXECUTABLE_IMAGE", - "IMAGE_FILE_LINE_NUMS_STRIPPED", - "IMAGE_FILE_LOCAL_SYMS_STRIPPED", - "IMAGE_FILE_AGGRESSIVE_WS_TRIM", - "IMAGE_FILE_LARGE_ADDRESS_AWARE", - "IMAGE_FILE_BYTES_REVERSED_LO", - "IMAGE_FILE_32BIT_MACHINE", - "IMAGE_FILE_DEBUG_STRIPPED", "IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP", - "IMAGE_FILE_NET_RUN_FROM_SWAP", "IMAGE_FILE_SYSTEM", - "IMAGE_FILE_DLL", "IMAGE_FILE_UP_SYSTEM_ONLY", - "IMAGE_FILE_BYTES_REVERSED_HI", "IMAGE_SCN_CNT_CODE", "IMAGE_SCN_CNT_INITIALIZED_DATA", "IMAGE_SCN_CNT_UNINITIALIZED_DATA", @@ -276,5 +273,8 @@ def decode_characteristics(characteristics: int) -> list[str]: "IMAGE_SCN_MEM_WRITE", "IMAGE_SYM_CLASS_EXTERNAL", "IMAGE_SYM_CLASS_STATIC", + "OPTIONAL_HDR_MAGIC_PE32", + "OPTIONAL_HDR_MAGIC_PE32_PLUS", + "PE_SIGNATURE", "decode_characteristics", ] diff --git a/objutils/pecoff/pdb/__init__.py b/objutils/pecoff/pdb/__init__.py index 7d4c933..cfa37e3 100644 --- a/objutils/pecoff/pdb/__init__.py +++ b/objutils/pecoff/pdb/__init__.py @@ -209,7 +209,7 @@ class ModuleInfo: base_of_dll: int size_of_image: int - entry_point: Optional[int] + entry_point: int | None # Types @@ -1441,7 +1441,7 @@ def pdb_symbols_for_pe(pe_path: str, symbol_path: str | None = None) -> list[dic """ except (OSError, RuntimeError, ValueError) as e: - print(f"Error: {str(e)}") + print(f"Error: {e!s}") return [] # Return an empty list in case of errors. diff --git a/objutils/rca.py b/objutils/rca.py index 61da5a5..7b0dc77 100644 --- a/objutils/rca.py +++ b/objutils/rca.py @@ -40,16 +40,16 @@ import io import re from collections.abc import Mapping, Sequence -from typing import Any, Optional +from typing import Any -import objutils.hexfile as hexfile +from objutils import hexfile # Record type identifiers DATA = 1 EOF = 2 # Regex to match header (null bytes + !M marker) -NULLS = re.compile(r"\0*\s*!M\s*(.*)", re.DOTALL | re.M) +NULLS = re.compile(r"\0*\s*!M\s*(.*)", re.DOTALL | re.MULTILINE) class Reader(hexfile.Reader): @@ -144,7 +144,7 @@ def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: """ return f"{address:04X} {Writer.hex_bytes(row)};" - def compose_header(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_header(self, meta: Mapping[str, Any]) -> str | None: """Compose header with separator and marker. Args: @@ -155,7 +155,7 @@ def compose_header(self, meta: Mapping[str, Any]) -> Optional[str]: """ return f"{Writer.SEPARATOR}!M" - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose footer with separator. Args: diff --git a/objutils/registry.py b/objutils/registry.py index cc13b10..d71b5ca 100644 --- a/objutils/registry.py +++ b/objutils/registry.py @@ -210,7 +210,6 @@ class CodecDoesNotExistError(Exception): print("Format not registered") """ - pass class CodecAlreadyExistError(Exception): @@ -230,7 +229,6 @@ class CodecAlreadyExistError(Exception): print("Intel HEX already registered") """ - pass class Codec(NamedTuple): @@ -351,7 +349,7 @@ def __init__(self): This is called only once due to singleton pattern. """ - self._codecs: "OrderedDict[str, Codec]" = OrderedDict() + self._codecs: OrderedDict[str, Codec] = OrderedDict() def __iter__(self) -> Iterator[tuple[str, Codec]]: """Iterate over (format_name, codec) pairs. diff --git a/objutils/scripts/oj_coff_extract.py b/objutils/scripts/oj_coff_extract.py index e2b5ceb..889f39b 100644 --- a/objutils/scripts/oj_coff_extract.py +++ b/objutils/scripts/oj_coff_extract.py @@ -66,7 +66,7 @@ def main(argv: list[str] | None = None) -> int: try: pp = PeParser(args.pe_file) except (OSError, ValueError, RuntimeError) as e: - print(f"\n'{args.pe_file}' is not valid PE/COFF file. Raised exception: '{repr(e)}'.") + print(f"\n'{args.pe_file}' is not valid PE/COFF file. Raised exception: '{e!r}'.") return 1 add_image_base = not args.no_image_base diff --git a/objutils/scripts/oj_coff_import.py b/objutils/scripts/oj_coff_import.py index 1d42f46..9e0ed52 100644 --- a/objutils/scripts/oj_coff_import.py +++ b/objutils/scripts/oj_coff_import.py @@ -39,7 +39,7 @@ def main(argv: list[str] | None = None) -> int: try: pp = PeParser(args.pe) except (OSError, ValueError, RuntimeError) as e: - print(f"\n'{args.pe}' is not a valid PE/COFF file. Raised exception: '{repr(e)}'.") + print(f"\n'{args.pe}' is not a valid PE/COFF file. Raised exception: '{e!r}'.") return 1 # Build DB and populate diff --git a/objutils/scripts/oj_coff_info.py b/objutils/scripts/oj_coff_info.py index 304177d..b8126a2 100644 --- a/objutils/scripts/oj_coff_info.py +++ b/objutils/scripts/oj_coff_info.py @@ -25,7 +25,7 @@ def main(argv: list[str] | None = None) -> int: try: pp = PeParser(args.pe_file, pdb_path=args.pdb_file) except (OSError, ValueError, RuntimeError) as e: - print(f"\n'{args.pe_file}' is not a valid PE/COFF file. Raised exception: '{repr(e)}'.") + print(f"\n'{args.pe_file}' is not a valid PE/COFF file. Raised exception: '{e!r}'.") return 1 machine = pp.machine() or 0 diff --git a/objutils/scripts/oj_coff_syms.py b/objutils/scripts/oj_coff_syms.py index c289ff7..9f25dee 100644 --- a/objutils/scripts/oj_coff_syms.py +++ b/objutils/scripts/oj_coff_syms.py @@ -31,7 +31,7 @@ def main(argv: list[str] | None = None) -> int: try: pp = PeParser(args.pe_file) except (OSError, ValueError, RuntimeError) as e: - print(f"\n'{args.pe_file}' is not a valid PE/COFF file. Raised exception: '{repr(e)}'.") + print(f"\n'{args.pe_file}' is not a valid PE/COFF file. Raised exception: '{e!r}'.") return 1 # Fetch via SymbolAPI to ensure DB is created/reused diff --git a/objutils/scripts/oj_elf_arm_attrs.py b/objutils/scripts/oj_elf_arm_attrs.py index 1d5446a..a6bdf5b 100644 --- a/objutils/scripts/oj_elf_arm_attrs.py +++ b/objutils/scripts/oj_elf_arm_attrs.py @@ -43,7 +43,7 @@ def main(): if ep.e_machine not in (defs.ELFMachineType.EM_ARM, defs.ELFMachineType.EM_AARCH64): print(f"\n'{args.elf_file}' is not an ARM architecture file.") exit(2) - print("") + print() arm_attrs = ep.arm_attributes if arm_attrs: for key, entries in arm_attrs.items(): diff --git a/objutils/scripts/oj_elf_extract.py b/objutils/scripts/oj_elf_extract.py index 60942b4..2ba3c0a 100644 --- a/objutils/scripts/oj_elf_extract.py +++ b/objutils/scripts/oj_elf_extract.py @@ -84,7 +84,7 @@ def main(): try: ep = ElfParser(args.elf_file) except (OSError, SQLAlchemyError, ValueError, RuntimeError) as e: - print(f"\n'{args.elf_file}' is not valid ELF file. Raised exception: '{repr(e)}'.") + print(f"\n'{args.elf_file}' is not valid ELF file. Raised exception: '{e!r}'.") exit(1) print("\nExtracting from...\n") print("Section Address Length") diff --git a/objutils/scripts/oj_elf_syms.py b/objutils/scripts/oj_elf_syms.py index be231cc..2322747 100644 --- a/objutils/scripts/oj_elf_syms.py +++ b/objutils/scripts/oj_elf_syms.py @@ -92,7 +92,7 @@ def main(): try: ep = ElfParser(args.elf_file) except (OSError, SQLAlchemyError, ValueError, RuntimeError) as e: - print(f"\n'{args.elf_file}' is not valid ELF file. Raised exception: '{repr(e)}'.") + print(f"\n'{args.elf_file}' is not valid ELF file. Raised exception: '{e!r}'.") exit(1) if args.symbol_list: symbol_list = args.symbol_list.split(",") @@ -108,7 +108,7 @@ def main(): symbol_list=symbol_list, ).items(): separator = "=" * len(section) - print("\n{1:}\n{0:}\n{1:}\n".format(section, separator)) + print(f"\n{separator}\n{section}\n{separator}\n") print("Name") print("Value Size Bind Type Access") print("-" * 79) @@ -119,14 +119,7 @@ def main(): "X" if sym.executeable else " ", ) print( - "{:30}\n{:08x} {:5d} {:10} {:9} {}\n".format( - sym.symbol_name, - sym.st_value, - sym.st_size, - sym.symbol_bind.name[4:], - sym.symbol_type.name[4:], - access_str, - ) + f"{sym.symbol_name:30}\n{sym.st_value:08x} {sym.st_size:5d} {sym.symbol_bind.name[4:]:10} {sym.symbol_type.name[4:]:9} {access_str}\n" ) diff --git a/objutils/section.py b/objutils/section.py index 68ae40b..69760c0 100644 --- a/objutils/section.py +++ b/objutils/section.py @@ -198,15 +198,15 @@ from dataclasses import dataclass from functools import reduce from operator import attrgetter, mul -from typing import Any, TextIO, Union +from typing import Any, TextIO import numpy as np -import objutils.hexdump as hexdump +from objutils import hexdump from objutils.exceptions import InvalidAddressError try: - from .hexfiles_ext import SequenceMatcher # noqa: F401 + from .hexfiles_ext import SequenceMatcher except ImportError: print("Error: cannot import `SequenceMatcher` from C++-extension, falling back to `difflib`") from difflib import SequenceMatcher # noqa: F401 @@ -367,7 +367,7 @@ def filler(ch: int, n: int) -> bytearray: return bytearray([ch] * n) -def _data_converter(data: Union[str, bytearray, array, Any]) -> bytearray: +def _data_converter(data: str | bytearray | array | Any) -> bytearray: if isinstance(data, bytearray): pass # no conversion needed. elif isinstance(data, int): @@ -741,7 +741,7 @@ def write(self, addr: int, data: bytes, **kws) -> None: raise InvalidAddressError(f"write(0x{addr:08x}) access out of bounds.") self.data[offset : offset + length] = data - def read_numeric(self, addr: int, dtype: str, **kws) -> Union[int, float]: + def read_numeric(self, addr: int, dtype: str, **kws) -> int | float: """Read a single numeric value with explicit endianness. Reads an integer or floating-point value from the specified address. @@ -802,7 +802,7 @@ def apply_bitmask(self, data: bytes, dtype: str, bit_mask: int) -> bytes: tmp &= bit_mask return tmp.to_bytes(data_size, byteorder, signed=False) - def write_numeric(self, addr: int, value: Union[int, float], dtype: str, **kws) -> None: + def write_numeric(self, addr: int, value: float, dtype: str, **kws) -> None: """Write a single numeric value with explicit endianness. Writes an integer or floating-point value to the specified address. @@ -840,7 +840,7 @@ def write_numeric(self, addr: int, value: Union[int, float], dtype: str, **kws) self.data[offset : offset + data_size] = struct.pack(fmt, value) - def read_numeric_array(self, addr: int, length: int, dtype: str, **kws) -> Union[list[int], list[float]]: + def read_numeric_array(self, addr: int, length: int, dtype: str, **kws) -> list[int] | list[float]: offset = addr - self.start_address if offset < 0: raise InvalidAddressError(f"read_numeric_array(0x{addr:08x}) access out of bounds.") @@ -851,7 +851,7 @@ def read_numeric_array(self, addr: int, length: int, dtype: str, **kws) -> Union data = self.data[offset : offset + data_size] return struct.unpack(fmt, data) - def read_asam_numeric(self, addr: int, dtype: str, byte_order: str = "MSB_LAST", **kws) -> Union[int, float]: + def read_asam_numeric(self, addr: int, dtype: str, byte_order: str = "MSB_LAST", **kws) -> int | float: asam_byte_order = self._resolve_asam_byteorder(byte_order) internal_dtype = self._asam_numeric_dtype_to_internal(dtype, asam_byte_order) value = self.read_numeric(addr, internal_dtype, **kws) @@ -866,7 +866,7 @@ def read_asam_numeric(self, addr: int, dtype: str, byte_order: str = "MSB_LAST", def write_asam_numeric( self, addr: int, - value: Union[int, float], + value: float, dtype: str, byte_order: str = "MSB_LAST", **kws, @@ -889,7 +889,7 @@ def read_asam_numeric_array( dtype: str, byte_order: str = "MSB_LAST", **kws, - ) -> Union[tuple[int, ...], tuple[float, ...]]: + ) -> tuple[int, ...] | tuple[float, ...]: asam_byte_order = self._resolve_asam_byteorder(byte_order) internal_dtype = self._asam_numeric_dtype_to_internal(dtype, asam_byte_order) fmt = self._getformat(internal_dtype, length) @@ -901,7 +901,7 @@ def read_asam_numeric_array( def write_asam_numeric_array( self, addr: int, - data: Union[list[int], list[float]], + data: list[int] | list[float], dtype: str, byte_order: str = "MSB_LAST", **kws, @@ -952,7 +952,7 @@ def write_asam_string(self, addr: int, value: str, dtype: str, **kws) -> None: self.data[offset : offset + len(encoded)] = encoded self.data[offset + len(encoded) : offset + total_length] = terminator - def write_numeric_array(self, addr: int, data: Union[list[int], list[float]], dtype: str, **kws) -> None: + def write_numeric_array(self, addr: int, data: list[int] | list[float], dtype: str, **kws) -> None: if not hasattr(data, "__iter__"): raise TypeError("data must be iterable") length = len(data) @@ -1193,11 +1193,7 @@ def find(self, expr: str, addr: int = -1) -> int: yield (self.start_address + item.start(), item.end() - item.start()) def __repr__(self) -> str: - return "Section(address = 0X{:08X}, length = {:d}, data = {})".format( - self.start_address, - self.length, - self.repr.repr(bytes(self.data)), - ) + return f"Section(address = 0X{self.start_address:08X}, length = {self.length:d}, data = {self.repr.repr(bytes(self.data))})" def __len__(self) -> int: return len(self.data) @@ -1261,10 +1257,10 @@ def __post_init__(self): def write(self, addr: int, data: bytes, **kws) -> None: raise NotImplementedError("LazySection is read-only") - def write_numeric(self, addr: int, value: Union[int, float], dtype: str, **kws) -> None: + def write_numeric(self, addr: int, value: float, dtype: str, **kws) -> None: raise NotImplementedError("LazySection is read-only") - def write_numeric_array(self, addr: int, data: Union[list[int], list[float]], dtype: str, **kws) -> None: + def write_numeric_array(self, addr: int, data: list[int] | list[float], dtype: str, **kws) -> None: raise NotImplementedError("LazySection is read-only") def write_string(self, addr: int, value: str, encoding: str = "latin1", **kws): diff --git a/objutils/shf.py b/objutils/shf.py index 5a915aa..27f6957 100644 --- a/objutils/shf.py +++ b/objutils/shf.py @@ -191,12 +191,7 @@ def dumps(self, image, **kws): sections = sorted(image.sections, key=lambda x: x.start_address) for idx, section in enumerate(sections): result.append( - ' '.format( - idx, - section.start_address, - section.length, - sha1_digest(section.data), - ) + f' ' ) nblocks = len(section.data) // BLOCK_SIZE remaining = len(section.data) % BLOCK_SIZE diff --git a/objutils/sig.py b/objutils/sig.py index cedcda6..b00a6a5 100644 --- a/objutils/sig.py +++ b/objutils/sig.py @@ -39,9 +39,9 @@ import io from collections.abc import Mapping, Sequence -from typing import Any, Optional +from typing import Any -import objutils.checksums as checksums +from objutils import checksums from objutils import hexfile, utils # Record type identifiers @@ -228,7 +228,7 @@ def compose_row(self, address: int, length: int, row: Sequence[int]) -> str: return line - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose EOF record with last address. Args: diff --git a/objutils/srec.py b/objutils/srec.py index 0e6606a..d6329dd 100644 --- a/objutils/srec.py +++ b/objutils/srec.py @@ -50,10 +50,10 @@ import re from collections.abc import Mapping, Sequence from functools import partial -from typing import Any, Optional +from typing import Any -import objutils.hexfile as hexfile -import objutils.utils as utils +from objutils import hexfile +from objutils import utils from objutils.checksums import COMPLEMENT_ONES, lrc from objutils.utils import make_list @@ -190,16 +190,8 @@ def special_processing(self, line: Any, format_type: int) -> None: pass elif format_type == S5: # print "S5: [%s]" % line.chunk - start_address = line.address # noqa: F841 - elif format_type == S7: - start_address = line.address # noqa: F841 - # print "Startaddress[S7]: %u" % start_address - # print "32-Bit Start-Address: ", hex(start_address) - elif format_type == S8: - start_address = line.address # noqa: F841 - # print "Startaddress[S8]: %u" % start_address - # print "24-Bit Start-Address: ", hex(start_address) - elif format_type == S9: + start_address = line.address + elif format_type == S7 or format_type == S8 or format_type == S9: start_address = line.address # noqa: F841 # print "Startaddress[S9]: %u" % start_address # print "16-Bit Start-Address: ", hex(start_address) @@ -235,9 +227,9 @@ class Writer(hexfile.Writer): termination records. """ - record_type: Optional[int] = None + record_type: int | None = None s5record: bool = False - start_address: Optional[int] = None + start_address: int | None = None MAX_ADDRESS_BITS = 32 @@ -272,7 +264,7 @@ def pre_processing(self, image: hexfile.Image) -> None: self.address_mask = f"%0{(self.record_type + 1) * 2:d}X" self.offset = self.record_type + 2 - def srecord(self, record_type: int, length: int, address: int, data: Optional[Sequence[int]] = None) -> str: + def srecord(self, record_type: int, length: int, address: int, data: Sequence[int] | None = None) -> str: """Generate a single S-Record line. Args: diff --git a/objutils/tek.py b/objutils/tek.py index 6a41660..3cdef12 100644 --- a/objutils/tek.py +++ b/objutils/tek.py @@ -39,11 +39,11 @@ """ from collections.abc import Mapping, Sequence -from typing import Any, Optional +from typing import Any -import objutils.checksums as checksums -import objutils.hexfile as hexfile -import objutils.utils as utils +from objutils import checksums +from objutils import hexfile +from objutils import utils # Record type identifiers DATA = 1 @@ -107,7 +107,7 @@ def __init__(self) -> None: super().__init__() self.last_address: int = 0 - def compose_footer(self, meta: Mapping[str, Any]) -> Optional[str]: + def compose_footer(self, meta: Mapping[str, Any]) -> str | None: """Compose EOF record with last address. Args: diff --git a/objutils/tests/test_image.py b/objutils/tests/test_image.py index 99a59b8..4bb9c10 100644 --- a/objutils/tests/test_image.py +++ b/objutils/tests/test_image.py @@ -1208,7 +1208,6 @@ def test_read_write_float64_array(): ) -# def test_write_uint8_negative_offset(): img = Image(Section(data=bytearray(10), start_address=0x1000)) with pytest.raises(InvalidAddressError): diff --git a/objutils/titxt.py b/objutils/titxt.py index 7cabb53..b83a97d 100644 --- a/objutils/titxt.py +++ b/objutils/titxt.py @@ -43,7 +43,7 @@ from typing import Any -import objutils.hexfile as hexfile +from objutils import hexfile class Reader(hexfile.ASCIIHexReader):