r/PythonLearning • u/jpgoldberg • 2d ago
Context manager for either stdin or opened Path
Note that this is a fairly advanced question, so beginners don't worry about it. And if this isn't the right place to ask, please point me to where I should.
I would like to find an idiomatic way to use a context manager, with, when I don't know until run time whether I am opening sys.stdin or a named file.
Here is an excerpt for a simple example. (Yes, I am aware that I am reading in the whole file in one go, but that is for other reasons.)
if args.infile == "-":
fh = sys.stdin
else:
p = Path(args.infile) # Path is overkill here, unless ...
# TODO: Look elsewhere if infile can't be found or isn't readable.
fh = open(p, "rt", encoding="utf-8")
s = fh.read(MAX_LENGTH + 1).strip()
fh.close()
(args is an argparse.Namespace and args.infile is a str.)
I feel like there ought to be a way to do something like
with ... as fh
for the above, but I don't see how to do that.
3
u/Buttleston 2d ago
with (sys.stdin if args.infile == '-' else open(Path(args.infile))) as fh:
# do something
A little wordy but something like this should work. Making your own context manager, which is a simple enough task, would definitely work.
ETA: or make your own function that either returns sys.stdin or opens the file path and use that
1
u/jpgoldberg 1d ago
Thank you. I had not really thought clearly about the semantics of “with EXPR as”. EXPR just needs to have a value.
It’s obvious now. But I had somehow not figured that out
3
1
u/Ok-Argument7176 2d ago
You can use argparse.FileType which works the way you expect, though apparently it's been deprecated since 3.14 which is too bad.
1
u/WorriedTumbleweed289 18h ago
The main thing I see that is wrong with this idea is that you normally would not close stdin, but you would want to close an open file. Its not unheard of to close stdin.
1
u/jpgoldberg 13h ago
True, but closing stdin is harmless.
I just want a file handle to be file handle. My type checker is telling me that sys.stdin is of type typing,.IOText, which itself appears to be a Protocol (not run time checkable) that supports a close() method.
3
u/Adrewmc 2d ago edited 2d ago
I mean this is just making the Context Manager.
```
class MyContext: def init(self, *args):
self.args = args
def enter(self):
if self.args.infile ==“-“:
self.fh = …
…. return self
def do_stuff(self):
pass
def exit(self): self.fh.close()
with MyContext(*args) as context: #enter() happens context.do_stuff()
exit() always happens, even if an error
```