1

I'm trying to write a small graph parser that reads from stdin and writes the processed output to stdout along the lines of:

# parser.py
G = defaultdict(list)
for line in sys.stdin:
    node, neighbor = line.split()
    G[node].append(neighbor)
print(G)

I would like to invoke the script with python -i parser.py < data.txt and interact with the objects I've created, but the interpreter always exits after the code runs even when I invoke Python with the -i option. N.B. The same thing occurs with ipython; it even confirms for me that I "really want to exit."

A workaround is to write the code to use a specific file passed in as an argument, but I was wondering if there is a way to make Python not exit the interpreter in the example above.

SSACody
  • 33
  • 2
  • 6

1 Answers1

3

The REPL (interactive console) exits when it exhausts standard input. Ordinarily, standard input is the console, so it only exits when you type ^D or call quit() manually. But if you redirect stdin from a file, stdin will be exhausted when you reach the end of the file.

You can use argparse to accept a file on the command line, defaulting to sys.stdin:

parser = argparse.ArgumentParser()
parser.add_argument('input', type=argparse.FileType(), nargs='?', default=sys.stdin)
args = parser.parse_args()
G = defaultdict(list)
for line in args.input:
    ...
Kevin
  • 28,963
  • 9
  • 62
  • 81
  • `for line in fileinput.input():` – jfs Feb 11 '15 at 20:31
  • @J.F.Sebastian: That's not extensible. If OP wants to add more arguments in the future, they'll need to rework `fileinput` based solutions. Such reworking risks breaking backwards compatibility (e.g. because someone wanted to pass a file named `-h`). – Kevin Feb 11 '15 at 20:36
  • @J.F.: Then you might as well just pass `sys.argv[1]` to `open()` or fall back to `sys.stdin`. I mean, you aren't going to need the multiple-file capabilities of `fileinput`, right? – Kevin Feb 11 '15 at 20:52
  • I see. This does feel like the correct way to do it; this lets me test small, manually constructed examples via `python -i parser.py` and on real data via `python -i parser.py file.txt`. – SSACody Feb 11 '15 at 20:56
  • @Kevin: `fileinput.input()` is the simplest thing that can accept input from files and fall back to `sys.stdin` automatically. – jfs Feb 11 '15 at 20:59
  • @J.F.Sebastian: And, as I explained, that will give you headaches if you ever add another command line switch. [YAGNI is not a free lunch](http://c2.com/cgi/wiki?EconomicsOfYagni). – Kevin Feb 11 '15 at 21:01
  • There is no compatibility issue. I would only pass `-h` as `-- -h` and `fileinput` does not support `--`. – jfs Feb 11 '15 at 21:06
  • @J.F.Sebastian: "and fileinput does not support --." -- that's precisely the problem. If you have an *actual file* named `-h`, you can *only* pass `-h`, because `-- -h` does not work. Then, you switch to argparse to support `--foo` or whatever, and `-h` breaks. – Kevin Feb 11 '15 at 21:07
  • yes. It is exactly the point if and when I need it; I'll implement it -- no sooner. – jfs Feb 11 '15 at 21:11
  • @J.F.Sebastian: The person passing arguments at the command line *is not you*. It's the end user, and they'll do whatever they damn well please, so long as it works. – Kevin Feb 11 '15 at 21:14
  • @Kevin: If we are talking about end users and backwards compatibility then you should add tests, documentation, etc to your answer. At this point I would use `argparse` – jfs Feb 11 '15 at 21:36
  • Can we have a reference to the Python documentation or code that justifies "The REPL (interactive console) exits when it exhausts standard input." ? – lordkrandel Jan 24 '22 at 08:31