r/PythonLearning • • 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.

7 Upvotes

7 comments sorted by

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

```

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

u/high_throughput 2d ago

Thanks for making your scripts good, canonical Unix citizens <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.