A lazy file works like a regular file but it does not fully open the file but it does perform some basic checks early to see if the filename parameter does make sense. This is useful for safely opening files for writing.
| 111 | |
| 112 | |
| 113 | class LazyFile: |
| 114 | """A lazy file works like a regular file but it does not fully open |
| 115 | the file but it does perform some basic checks early to see if the |
| 116 | filename parameter does make sense. This is useful for safely opening |
| 117 | files for writing. |
| 118 | """ |
| 119 | |
| 120 | name: str |
| 121 | mode: str |
| 122 | encoding: str | None |
| 123 | errors: str | None |
| 124 | atomic: bool |
| 125 | _f: t.IO[t.Any] | None |
| 126 | should_close: bool |
| 127 | |
| 128 | def __init__( |
| 129 | self, |
| 130 | filename: str | os.PathLike[str], |
| 131 | mode: str = "r", |
| 132 | encoding: str | None = None, |
| 133 | errors: str | None = "strict", |
| 134 | atomic: bool = False, |
| 135 | ) -> None: |
| 136 | self.name = os.fspath(filename) |
| 137 | self.mode = mode |
| 138 | self.encoding = encoding |
| 139 | self.errors = errors |
| 140 | self.atomic = atomic |
| 141 | |
| 142 | if self.name == "-": |
| 143 | self._f, self.should_close = open_stream(filename, mode, encoding, errors) |
| 144 | else: |
| 145 | if "r" in mode: |
| 146 | # Open and close the file in case we're opening it for |
| 147 | # reading so that we can catch at least some errors in |
| 148 | # some cases early. |
| 149 | open(filename, mode).close() |
| 150 | self._f = None |
| 151 | self.should_close = True |
| 152 | |
| 153 | def __getattr__(self, name: str) -> t.Any: |
| 154 | return getattr(self.open(), name) |
| 155 | |
| 156 | def __repr__(self) -> str: |
| 157 | if self._f is not None: |
| 158 | return repr(self._f) |
| 159 | return f"<unopened file '{format_filename(self.name)}' {self.mode}>" |
| 160 | |
| 161 | def open(self) -> t.IO[t.Any]: |
| 162 | """Opens the file if it's not yet open. This call might fail with |
| 163 | a :exc:`FileError`. Not handling this error will produce an error |
| 164 | that Click shows. |
| 165 | """ |
| 166 | if self._f is not None: |
| 167 | return self._f |
| 168 | try: |
| 169 | rv, self.should_close = open_stream( |
| 170 | self.name, self.mode, self.encoding, self.errors, atomic=self.atomic |