1# coding: utf-8 2# 3# © 2021 Qualcomm Innovation Center, Inc. All rights reserved. 4# 5# SPDX-License-Identifier: BSD-3-Clause 6 7""" 8""" 9 10import sys 11import io 12import argparse 13 14 15class _ReplaceFileMixin(object): 16 def __init__(self, name, mode, encoding=None): 17 super().__init__() 18 self._name = name 19 self._mode = mode 20 self._encoding = encoding 21 22 @property 23 def name(self): 24 return self._name 25 26 def close(self): 27 tmp = io.open(self._name, self._mode.replace('w', 'r'), 28 encoding=self._encoding) 29 old = tmp.read() 30 tmp.close() 31 self.seek(0) 32 new = self.read() 33 if old != new: 34 replace = io.open(self._name, self._mode, encoding=self._encoding) 35 replace.write(new) 36 replace.close() 37 38 def __enter__(self): 39 return self 40 41 def __exit__(self, exc_type, exc_val, exc_tb): 42 self.close() 43 44 45class _ReplaceBinaryFile(_ReplaceFileMixin, io.BytesIO): 46 pass 47 48 49class _ReplaceTextFile(_ReplaceFileMixin, io.StringIO): 50 pass 51 52 53class _GenFileFactory(object): 54 def __init__(self, mode, encoding=None): 55 self._mode = mode 56 if mode not in ('w', 'wt', 'wb'): 57 raise ValueError("mode {:s} not supported".format(mode)) 58 if encoding is None and 'b' not in mode: 59 # Default to UTF-8 for text files 60 encoding = 'utf-8' 61 self._encoding = encoding 62 63 def __call__(self, p): 64 if sys.hexversion < 0x03030000: 65 # Exclusive file creation ('x' mode) isn't available before Python 66 # 3.3, so fall back to just replacing the file. 67 return io.open(p, self._mode, encoding=self._encoding) 68 69 try: 70 return io.open(p, self._mode.replace('w', 'x'), 71 encoding=self._encoding) 72 except FileExistsError: 73 if 'b' in self._mode: 74 return _ReplaceBinaryFile(p, self._mode) 75 else: 76 return _ReplaceTextFile(p, self._mode, encoding=self._encoding) 77 78 79class GenFileType(_GenFileFactory, argparse.FileType): 80 def __call__(self, p): 81 if p == '-': 82 assert 'w' in self._mode 83 return sys.stdout 84 try: 85 return super().__call__(p) 86 except OSError as e: 87 raise argparse.ArgumentTypeError( 88 "can't open {:s}: {:s}".format(p, str(e))) 89 90 91def GenFile(name, mode, encoding=None): 92 return _GenFileFactory(mode, encoding=encoding)(name) 93