Coverage for klayout_pex/klayout/output_files.py: 100%

14 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-06 13:14 +0000

1# 

2# -------------------------------------------------------------------------------- 

3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl 

4# Johannes Kepler University, Institute for Integrated Circuits. 

5# 

6# This file is part of KPEX 

7# (see https://github.com/iic-jku/klayout-pex). 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21# SPDX-License-Identifier: GPL-3.0-or-later 

22# -------------------------------------------------------------------------------- 

23# 

24 

25import gzip 

26import os 

27import shutil 

28import tempfile 

29from typing import * 

30 

31 

32def write_output_file(write: Callable[[str], object], path: str): 

33 """ 

34 Writes a file with a writer of KLayout (e.g. Layout.write, ReportDatabase.save), a .gz one compressed by Python: 

35 on Windows, KLayout leaks a C runtime stream with each gzip file it writes (_wfopen without fclose), 

36 of which a process has 512 (#361) 

37 

38 :param write: the writer, called with the path to write to 

39 :param path: e.g. cell.gds.gz, or cell.oas, which the writer writes itself 

40 """ 

41 if not path.lower().endswith('.gz'): 

42 write(path) 

43 return 

44 with tempfile.TemporaryDirectory() as tmp_dir: 

45 # NOTE: without .gz, but with the suffix that selects the format (e.g. .gds) 

46 plain_path = os.path.join(tmp_dir, os.path.basename(path)[:-len('.gz')]) 

47 write(plain_path) 

48 with open(plain_path, 'rb') as plain, gzip.open(path, 'wb') as compressed: 

49 shutil.copyfileobj(plain, compressed)