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