Last active
May 1, 2024 21:05
-
-
Save angstwad/e4bdaa249158fbe1f1ddfb0e9327b9e5 to your computer and use it in GitHub Desktop.
Better argparse.FileType - lazily open files passed as arguments rather than them being opened immediately
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2024 Paul Durivage <[email protected]> | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
class LazyFileType(argparse.FileType): | |
"""Subclasses `argparse.FileType` in order to provide a way to lazily open | |
files for reading/writing from arguments. Initializes the same as the | |
parent, but provides `open` method which returns the file object. | |
Usage: | |
``` | |
parser = argparse.ArgumentParser() | |
parser.add_argument('f', type=LazyFileType('w')) | |
args = parser.parse_args() | |
with args.f.open() as f: | |
for line in foo: | |
... | |
``` | |
Provides an alternate constructor for use with the `default` kwarg to | |
`ArgumentParser.add_argument`. | |
Usage: | |
``` | |
parser.add_argument('-f', type=LazyFileType('w'), | |
default=LazyFileType.default('some_file.txt') | |
``` | |
""" | |
def __call__(self, string: str) -> typing.Self: # type: ignore | |
self.filename = string # pylint: disable=attribute-defined-outside-init | |
if 'r' in self._mode or 'x' in self._mode: | |
if not pathlib.Path(self.filename).exists(): | |
m = (f"can't open {self.filename}: No such file or directory: " | |
f"'{self.filename}'") | |
raise argparse.ArgumentTypeError(m) | |
return self | |
def open(self) -> typing.IO: | |
"""Opens and returns file for reading | |
:rtype: io.TextIOWrapper | |
""" | |
return open(self.filename, self._mode, self._bufsize, self._encoding, | |
self._errors) | |
@classmethod | |
def default(cls, string: str, **kwargs) -> typing.Self: | |
"""Alternate constructor for a default argument to argparse argument | |
Args: | |
string: filename to open | |
**kwargs: arguments to `__init__` | |
Returns: | |
instance of `LazyFileType` | |
""" | |
inst = cls(**kwargs) | |
inst.filename = string # pylint: disable=attribute-defined-outside-init | |
return inst |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment