From 1872df4a339b6c3a9e04dd0b55564902871f412d Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 6 Feb 2022 14:20:13 +0100 Subject: [PATCH] Make split_commands faster --- examples/bench.py | 60 ++++++++++++++++++++++++++++++++++++++ examples/load_directory.py | 11 +++++++ gerbonara/excellon.py | 4 ++- gerbonara/rs274x.py | 27 ++++++++++++----- 4 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 examples/bench.py create mode 100644 examples/load_directory.py diff --git a/examples/bench.py b/examples/bench.py new file mode 100644 index 0000000..b8d8c36 --- /dev/null +++ b/examples/bench.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +import time +from pathlib import Path + +import tqdm + +import gerbonara + +if __name__ == '__main__': + resources = Path(__file__).parent.parent / 'gerbonara' / 'tests' / 'resources' + + TEST_FILES = [ + 'easyeda/Gerber_TopSilkLayer.GTO', + 'allegro-2/MinnowMax_RevA1_GAF_Gerber/MinnowMax_lyr3.art', + 'allegro-2/MinnowMax_RevA1_GAF_Gerber/MinnowMax_fab.art', + 'eagle-newer/soldermask_bottom.gbr', + 'eagle-newer/copper_bottom.gbr', +# FIXME remove redundant warnings in these files +# 'siemens/80101_0125_F200_SilkscreenBottom.gdo', +# 'siemens/80101_0125_F200_SoldermaskBottom.gdo', +# 'siemens/80101_0125_F200_SolderPasteBottom.gdo', +# 'siemens/80101_0125_F200_L03.gdo', +# 'siemens/80101_0125_F200_L01_Top.gdo', +# 'zuken-emulated/Gerber/Symbol-A.fph', + 'Target3001/IRNASIoTbank1.2.StopTop', + 'Target3001/IRNASIoTbank1.2.PasteTop', + 'Target3001/IRNASIoTbank1.2.PasteBot', + 'Target3001/IRNASIoTbank1.2.StopBot', + 'Target3001/IRNASIoTbank1.2.Top', + 'pcb-rnd/power-art.gtl', + 'pcb-rnd/power-art.gto', + 'pcb-rnd/power-art.gtp', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G1', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G2', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G3', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G4', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G5', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G6', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G7', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G8', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G9', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G10', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G11', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.G12', + 'altium-composite-drill/Gerber/LimeSDR-QPCIe_1v2.GBS', + 'eagle_files/copper_top_l1.gbr', + 'eagle_files/soldermask_top.gbr', + 'diptrace/mainboard_Bottom.gbr', + 'upverter/design_export.gtp', + 'upverter/design_export.gbl', + ] + + start = time.perf_counter() + for file in TEST_FILES: #tqdm.tqdm(TEST_FILES): + gerbonara.GerberFile.open(resources / file) + end = time.perf_counter() + + print(f'Duration: {(end - start)*1000:.3f} ms') + diff --git a/examples/load_directory.py b/examples/load_directory.py new file mode 100644 index 0000000..6e1f901 --- /dev/null +++ b/examples/load_directory.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument('input', help='Input Gerber directory') + args = parser.parse_args() + + import gerbonara + print(gerbonara.LayerStack.from_directory(args.input)) + diff --git a/gerbonara/excellon.py b/gerbonara/excellon.py index 5f0db44..721fcca 100755 --- a/gerbonara/excellon.py +++ b/gerbonara/excellon.py @@ -535,6 +535,7 @@ class ExcellonParser(object): self.lineno = None self.filename = None self.external_tools = external_tools or {} + self.found_kicad_format_comment = False def warn(self, msg): warnings.warn(f'{self.filename}:{self.lineno} "{self.line}": {msg}', SyntaxWarning) @@ -844,7 +845,7 @@ class ExcellonParser(object): integer, _, fractional = match[3][1:].partition('.') self.settings.number_format = len(integer), len(fractional) - elif self.settings.number_format == (None, None) and not metric: + elif self.settings.number_format == (None, None) and not metric and not self.found_kicad_format_comment: self.warn('Using implicit number format from bare "INCH" statement. This is normal for Fritzing, Diptrace, Geda and pcb-rnd.') self.settings.number_format = (2,4) @@ -981,6 +982,7 @@ class ExcellonParser(object): x = None if x == '-' else int(x) y = None if y == '-' else int(y) self.settings.number_format = x, y + self.found_kicad_format_comment = True self.settings.notation = match[2] self.settings.unit = Inch if match[3] == 'inch' else MM diff --git a/gerbonara/rs274x.py b/gerbonara/rs274x.py index 82ee047..e29ecd8 100644 --- a/gerbonara/rs274x.py +++ b/gerbonara/rs274x.py @@ -520,11 +520,11 @@ class GerberParser: NAME = r"[a-zA-Z_$\.][a-zA-Z_$\.0-9+\-]+" STATEMENT_REGEXES = { - 'region_start': r'G36$', - 'region_end': r'G37$', 'coord': fr"(?PG0?[123]|G74|G75|G54|G55)?(X(?P{NUMBER}))?(Y(?P{NUMBER}))?" \ fr"(I(?P{NUMBER}))?(J(?P{NUMBER}))?" \ fr"(?PD0?[123])?$", + 'region_start': r'G36$', + 'region_end': r'G37$', 'aperture': r"(G54|G55)?D(?P\d+)", # Allegro combines format spec and unit into one long illegal extended command. 'allegro_format_spec': r"FS(?P(L|T|D))?(?P(A|I))[NG0-9]*X(?P[0-7][0-7])Y(?P[0-7][0-7])[DM0-9]*\*MO(?PIN|MM)", @@ -581,12 +581,25 @@ class GerberParser: self.lineno = None self.line = None + def _shorten_line(self): + line_joined = self.line.replace('\r', '').replace('\n', '\\n') + if len(line_joined) > 80: + return f'{line_joined[:20]}[...]{line_joined[-20:]}' + else: + return line_joined + def warn(self, msg, kls=SyntaxWarning): - line_joined = self.line.replace('\n', '\\n') - warnings.warn(f'{self.filename}:{self.lineno} "{line_joined}": {msg}', kls) + warnings.warn(f'{self.filename}:{self.lineno} "{self._shorten_line()}": {msg}', kls) @classmethod def _split_commands(kls, data): + for match in re.finditer(r'G04.*?\*|%.*?%|[^*%]*\*', data, re.DOTALL): + cmd = match[0].strip().strip('%').rstrip('*').replace('\r', '').replace('\n', '') + if cmd: + yield 1, cmd + return + + ####### start = 0 extended_command = False lineno = 1 @@ -637,13 +650,13 @@ class GerberParser: try: getattr(self, f'_parse_{name}')(match) except Exception as e: - raise SyntaxError(f'{filename}:{lineno} "{line}": {e}') from e + raise SyntaxError(f'{filename}:{lineno} "{self._shorten_line()}": {e}') from e line = line[match.end(0):] break else: - self.warn(f'Unknown statement found: "{line}", ignoring.', UnknownStatementWarning) - self.target.comments.append(f'Unknown statement found: "{line}", ignoring.') + self.warn(f'Unknown statement found: "{self._shorten_line()}", ignoring.', UnknownStatementWarning) + self.target.comments.append(f'Unknown statement found: "{self._shorten_line()}", ignoring.') self.target.apertures = list(self.aperture_map.values()) self.target.import_settings = self.file_settings