Skip to content

Commit 61aa87e

Browse files
ESultanikclaude
andcommitted
perf: lazy load Kaitai parsers and remove unused imports
Make Kaitai struct parsers lazy-loaded by deferring the KaitaiParser.load() call until the parser is first invoked. Previously, all 31 Kaitai parsers were loaded at import time even if the file type was never encountered. Also removes unused imports (Gif, Jpeg, Png) that were imported at module level but never used. Changes: - Add LazyKaitaiParser class that wraps parser loading - Remove unused Gif, Jpeg, Png imports from kaitaimatcher.py - Parsers are now loaded on-demand when first parsing a matching file Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent abaef67 commit 61aa87e

1 file changed

Lines changed: 30 additions & 23 deletions

File tree

polyfile/kaitaimatcher.py

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
from kaitaistruct import KaitaiStruct, KaitaiStructError
66

77
from .kaitai.parser import ASTNode, KaitaiParser, RootNode
8-
from .kaitai.parsers.gif import Gif
9-
from .kaitai.parsers.jpeg import Jpeg
10-
from .kaitai.parsers.png import Png
118
from .logger import getStatusLogger
129
from .polyfile import register_parser, InvalidMatch, Match, Parser, Submatch
1310

@@ -83,31 +80,41 @@ def ast_to_matches(ast: RootNode, parent: Match) -> Iterator[Submatch]:
8380
stack.extend(reversed([(new_node, c) for c in node.children]))
8481

8582

86-
for mimetype, kaitai_path in KAITAI_MIME_MAPPING.items():
87-
class parse_:
88-
kaitai_parser = KaitaiParser.load(kaitai_path)
89-
90-
def __call__(self, stream, match):
91-
try:
92-
ast = self.kaitai_parser.parse(stream).ast
93-
except KaitaiStructError as e:
94-
log.warning(f"Error parsing {stream.name} using {self.kaitai_parser}: {e!s}")
95-
raise InvalidMatch()
96-
except Exception as e:
97-
log.error(f"Unexpected exception parsing {stream.name} using {self.kaitai_parser}: {e!s}")
98-
raise InvalidMatch()
99-
yield from ast_to_matches(ast, parent=match)
83+
class LazyKaitaiParser:
84+
"""Parser that lazily loads the Kaitai struct parser on first use."""
10085

101-
func_name = mimetype.replace("/", "_").replace("-", "_")
86+
def __init__(self, kaitai_path: str, mimetype: str):
87+
self.kaitai_path = kaitai_path
88+
self.mimetype = mimetype
89+
self._kaitai_parser = None
90+
91+
@property
92+
def kaitai_parser(self):
93+
if self._kaitai_parser is None:
94+
self._kaitai_parser = KaitaiParser.load(self.kaitai_path)
95+
MIME_BY_PARSER[self._kaitai_parser.struct_type] = self.mimetype
96+
return self._kaitai_parser
10297

103-
parse_.__name__ = f"{parse_.__name__}{func_name}"
104-
parse_.__qualname__ = f"{parse_.__qualname__}{func_name}"
98+
def __call__(self, stream, match):
99+
try:
100+
ast = self.kaitai_parser.parse(stream).ast
101+
except KaitaiStructError as e:
102+
log.warning(f"Error parsing {stream.name} using {self.kaitai_parser}: {e!s}")
103+
raise InvalidMatch()
104+
except Exception as e:
105+
log.error(f"Unexpected exception parsing {stream.name} using {self.kaitai_parser}: {e!s}")
106+
raise InvalidMatch()
107+
yield from ast_to_matches(ast, parent=match)
105108

106-
register_parser(mimetype)(parse_())
107109

108-
MIME_BY_PARSER[parse_.kaitai_parser.struct_type] = mimetype
110+
for mimetype, kaitai_path in KAITAI_MIME_MAPPING.items():
111+
func_name = mimetype.replace("/", "_").replace("-", "_")
112+
parser = LazyKaitaiParser(kaitai_path, mimetype)
113+
parser.__name__ = f"parse_{func_name}"
114+
parser.__qualname__ = f"parse_{func_name}"
115+
register_parser(mimetype)(parser)
109116

110117
del func_name
111118
del kaitai_path
112119
del mimetype
113-
del parse_
120+
del parser

0 commit comments

Comments
 (0)