1 """Helper class to quickly write a loop over all standard input files.
2
3 Typical use is:
4
5 import fileinput
6 for line in fileinput.input(encoding="utf-8"):
7 process(line)
8
9 This iterates over the lines of all files listed in sys.argv[1:],
10 defaulting to sys.stdin if the list is empty. If a filename is '-' it
11 is also replaced by sys.stdin and the optional arguments mode and
12 openhook are ignored. To specify an alternative list of filenames,
13 pass it as the argument to input(). A single file name is also allowed.
14
15 Functions filename(), lineno() return the filename and cumulative line
16 number of the line that has just been read; filelineno() returns its
17 line number in the current file; isfirstline() returns true iff the
18 line just read is the first line of its file; isstdin() returns true
19 iff the line was read from sys.stdin. Function nextfile() closes the
20 current file so that the next iteration will read the first line from
21 the next file (if any); lines not read from the file will not count
22 towards the cumulative line count; the filename is not changed until
23 after the first line of the next file has been read. Function close()
24 closes the sequence.
25
26 Before any lines have been read, filename() returns None and both line
27 numbers are zero; nextfile() has no effect. After all lines have been
28 read, filename() and the line number functions return the values
29 pertaining to the last line read; nextfile() has no effect.
30
31 All files are opened in text mode by default, you can override this by
32 setting the mode parameter to input() or FileInput.__init__().
33 If an I/O error occurs during opening or reading a file, the OSError
34 exception is raised.
35
36 If sys.stdin is used more than once, the second and further use will
37 return no lines, except perhaps for interactive use, or if it has been
38 explicitly reset (e.g. using sys.stdin.seek(0)).
39
40 Empty files are opened and immediately closed; the only time their
41 presence in the list of filenames is noticeable at all is when the
42 last file opened is empty.
43
44 It is possible that the last line of a file doesn't end in a newline
45 character; otherwise lines are returned including the trailing
46 newline.
47
48 Class FileInput is the implementation; its methods filename(),
49 lineno(), fileline(), isfirstline(), isstdin(), nextfile() and close()
50 correspond to the functions in the module. In addition it has a
51 readline() method which returns the next input line, and a
52 __getitem__() method which implements the sequence behavior. The
53 sequence must be accessed in strictly sequential order; sequence
54 access and readline() cannot be mixed.
55
56 Optional in-place filtering: if the keyword argument inplace=1 is
57 passed to input() or to the FileInput constructor, the file is moved
58 to a backup file and standard output is directed to the input file.
59 This makes it possible to write a filter that rewrites its input file
60 in place. If the keyword argument backup=".<some extension>" is also
61 given, it specifies the extension for the backup file, and the backup
62 file remains around; by default, the extension is ".bak" and it is
63 deleted when the output file is closed. In-place filtering is
64 disabled when standard input is read. XXX The current implementation
65 does not work for MS-DOS 8+3 filesystems.
66 """
67
68 import io
69 import sys, os
70 from types import GenericAlias
71
72 __all__ = ["input", "close", "nextfile", "filename", "lineno", "filelineno",
73 "fileno", "isfirstline", "isstdin", "FileInput", "hook_compressed",
74 "hook_encoded"]
75
76 _state = None
77
78 def input(files=None, inplace=False, backup="", *, mode="r", openhook=None,
79 encoding=None, errors=None):
80 """Return an instance of the FileInput class, which can be iterated.
81
82 The parameters are passed to the constructor of the FileInput class.
83 The returned instance, in addition to being an iterator,
84 keeps global state for the functions of this module,.
85 """
86 global _state
87 if _state and _state._file:
88 raise RuntimeError("input() already active")
89 _state = FileInput(files, inplace, backup, mode=mode, openhook=openhook,
90 encoding=encoding, errors=errors)
91 return _state
92
93 def close():
94 """Close the sequence."""
95 global _state
96 state = _state
97 _state = None
98 if state:
99 state.close()
100
101 def nextfile():
102 """
103 Close the current file so that the next iteration will read the first
104 line from the next file (if any); lines not read from the file will
105 not count towards the cumulative line count. The filename is not
106 changed until after the first line of the next file has been read.
107 Before the first line has been read, this function has no effect;
108 it cannot be used to skip the first file. After the last line of the
109 last file has been read, this function has no effect.
110 """
111 if not _state:
112 raise RuntimeError("no active input()")
113 return _state.nextfile()
114
115 def filename():
116 """
117 Return the name of the file currently being read.
118 Before the first line has been read, returns None.
119 """
120 if not _state:
121 raise RuntimeError("no active input()")
122 return _state.filename()
123
124 def lineno():
125 """
126 Return the cumulative line number of the line that has just been read.
127 Before the first line has been read, returns 0. After the last line
128 of the last file has been read, returns the line number of that line.
129 """
130 if not _state:
131 raise RuntimeError("no active input()")
132 return _state.lineno()
133
134 def filelineno():
135 """
136 Return the line number in the current file. Before the first line
137 has been read, returns 0. After the last line of the last file has
138 been read, returns the line number of that line within the file.
139 """
140 if not _state:
141 raise RuntimeError("no active input()")
142 return _state.filelineno()
143
144 def fileno():
145 """
146 Return the file number of the current file. When no file is currently
147 opened, returns -1.
148 """
149 if not _state:
150 raise RuntimeError("no active input()")
151 return _state.fileno()
152
153 def isfirstline():
154 """
155 Returns true the line just read is the first line of its file,
156 otherwise returns false.
157 """
158 if not _state:
159 raise RuntimeError("no active input()")
160 return _state.isfirstline()
161
162 def isstdin():
163 """
164 Returns true if the last line was read from sys.stdin,
165 otherwise returns false.
166 """
167 if not _state:
168 raise RuntimeError("no active input()")
169 return _state.isstdin()
170
171 class ESC[4;38;5;81mFileInput:
172 """FileInput([files[, inplace[, backup]]], *, mode=None, openhook=None)
173
174 Class FileInput is the implementation of the module; its methods
175 filename(), lineno(), fileline(), isfirstline(), isstdin(), fileno(),
176 nextfile() and close() correspond to the functions of the same name
177 in the module.
178 In addition it has a readline() method which returns the next
179 input line, and a __getitem__() method which implements the
180 sequence behavior. The sequence must be accessed in strictly
181 sequential order; random access and readline() cannot be mixed.
182 """
183
184 def __init__(self, files=None, inplace=False, backup="", *,
185 mode="r", openhook=None, encoding=None, errors=None):
186 if isinstance(files, str):
187 files = (files,)
188 elif isinstance(files, os.PathLike):
189 files = (os.fspath(files), )
190 else:
191 if files is None:
192 files = sys.argv[1:]
193 if not files:
194 files = ('-',)
195 else:
196 files = tuple(files)
197 self._files = files
198 self._inplace = inplace
199 self._backup = backup
200 self._savestdout = None
201 self._output = None
202 self._filename = None
203 self._startlineno = 0
204 self._filelineno = 0
205 self._file = None
206 self._isstdin = False
207 self._backupfilename = None
208 self._encoding = encoding
209 self._errors = errors
210
211 # We can not use io.text_encoding() here because old openhook doesn't
212 # take encoding parameter.
213 if (sys.flags.warn_default_encoding and
214 "b" not in mode and encoding is None and openhook is None):
215 import warnings
216 warnings.warn("'encoding' argument not specified.",
217 EncodingWarning, 2)
218
219 # restrict mode argument to reading modes
220 if mode not in ('r', 'rb'):
221 raise ValueError("FileInput opening mode must be 'r' or 'rb'")
222 self._mode = mode
223 self._write_mode = mode.replace('r', 'w')
224 if openhook:
225 if inplace:
226 raise ValueError("FileInput cannot use an opening hook in inplace mode")
227 if not callable(openhook):
228 raise ValueError("FileInput openhook must be callable")
229 self._openhook = openhook
230
231 def __del__(self):
232 self.close()
233
234 def close(self):
235 try:
236 self.nextfile()
237 finally:
238 self._files = ()
239
240 def __enter__(self):
241 return self
242
243 def __exit__(self, type, value, traceback):
244 self.close()
245
246 def __iter__(self):
247 return self
248
249 def __next__(self):
250 while True:
251 line = self._readline()
252 if line:
253 self._filelineno += 1
254 return line
255 if not self._file:
256 raise StopIteration
257 self.nextfile()
258 # repeat with next file
259
260 def nextfile(self):
261 savestdout = self._savestdout
262 self._savestdout = None
263 if savestdout:
264 sys.stdout = savestdout
265
266 output = self._output
267 self._output = None
268 try:
269 if output:
270 output.close()
271 finally:
272 file = self._file
273 self._file = None
274 try:
275 del self._readline # restore FileInput._readline
276 except AttributeError:
277 pass
278 try:
279 if file and not self._isstdin:
280 file.close()
281 finally:
282 backupfilename = self._backupfilename
283 self._backupfilename = None
284 if backupfilename and not self._backup:
285 try: os.unlink(backupfilename)
286 except OSError: pass
287
288 self._isstdin = False
289
290 def readline(self):
291 while True:
292 line = self._readline()
293 if line:
294 self._filelineno += 1
295 return line
296 if not self._file:
297 return line
298 self.nextfile()
299 # repeat with next file
300
301 def _readline(self):
302 if not self._files:
303 if 'b' in self._mode:
304 return b''
305 else:
306 return ''
307 self._filename = self._files[0]
308 self._files = self._files[1:]
309 self._startlineno = self.lineno()
310 self._filelineno = 0
311 self._file = None
312 self._isstdin = False
313 self._backupfilename = 0
314
315 # EncodingWarning is emitted in __init__() already
316 if "b" not in self._mode:
317 encoding = self._encoding or "locale"
318 else:
319 encoding = None
320
321 if self._filename == '-':
322 self._filename = '<stdin>'
323 if 'b' in self._mode:
324 self._file = getattr(sys.stdin, 'buffer', sys.stdin)
325 else:
326 self._file = sys.stdin
327 self._isstdin = True
328 else:
329 if self._inplace:
330 self._backupfilename = (
331 os.fspath(self._filename) + (self._backup or ".bak"))
332 try:
333 os.unlink(self._backupfilename)
334 except OSError:
335 pass
336 # The next few lines may raise OSError
337 os.rename(self._filename, self._backupfilename)
338 self._file = open(self._backupfilename, self._mode,
339 encoding=encoding, errors=self._errors)
340 try:
341 perm = os.fstat(self._file.fileno()).st_mode
342 except OSError:
343 self._output = open(self._filename, self._write_mode,
344 encoding=encoding, errors=self._errors)
345 else:
346 mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
347 if hasattr(os, 'O_BINARY'):
348 mode |= os.O_BINARY
349
350 fd = os.open(self._filename, mode, perm)
351 self._output = os.fdopen(fd, self._write_mode,
352 encoding=encoding, errors=self._errors)
353 try:
354 os.chmod(self._filename, perm)
355 except OSError:
356 pass
357 self._savestdout = sys.stdout
358 sys.stdout = self._output
359 else:
360 # This may raise OSError
361 if self._openhook:
362 # Custom hooks made previous to Python 3.10 didn't have
363 # encoding argument
364 if self._encoding is None:
365 self._file = self._openhook(self._filename, self._mode)
366 else:
367 self._file = self._openhook(
368 self._filename, self._mode, encoding=self._encoding, errors=self._errors)
369 else:
370 self._file = open(self._filename, self._mode, encoding=encoding, errors=self._errors)
371 self._readline = self._file.readline # hide FileInput._readline
372 return self._readline()
373
374 def filename(self):
375 return self._filename
376
377 def lineno(self):
378 return self._startlineno + self._filelineno
379
380 def filelineno(self):
381 return self._filelineno
382
383 def fileno(self):
384 if self._file:
385 try:
386 return self._file.fileno()
387 except ValueError:
388 return -1
389 else:
390 return -1
391
392 def isfirstline(self):
393 return self._filelineno == 1
394
395 def isstdin(self):
396 return self._isstdin
397
398 __class_getitem__ = classmethod(GenericAlias)
399
400
401 def hook_compressed(filename, mode, *, encoding=None, errors=None):
402 if encoding is None and "b" not in mode: # EncodingWarning is emitted in FileInput() already.
403 encoding = "locale"
404 ext = os.path.splitext(filename)[1]
405 if ext == '.gz':
406 import gzip
407 stream = gzip.open(filename, mode)
408 elif ext == '.bz2':
409 import bz2
410 stream = bz2.BZ2File(filename, mode)
411 else:
412 return open(filename, mode, encoding=encoding, errors=errors)
413
414 # gzip and bz2 are binary mode by default.
415 if "b" not in mode:
416 stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors)
417 return stream
418
419
420 def hook_encoded(encoding, errors=None):
421 def openhook(filename, mode):
422 return open(filename, mode, encoding=encoding, errors=errors)
423 return openhook
424
425
426 def _test():
427 import getopt
428 inplace = False
429 backup = False
430 opts, args = getopt.getopt(sys.argv[1:], "ib:")
431 for o, a in opts:
432 if o == '-i': inplace = True
433 if o == '-b': backup = a
434 for line in input(args, inplace=inplace, backup=backup):
435 if line[-1:] == '\n': line = line[:-1]
436 if line[-1:] == '\r': line = line[:-1]
437 print("%d: %s[%d]%s %s" % (lineno(), filename(), filelineno(),
438 isfirstline() and "*" or "", line))
439 print("%d: %s[%d]" % (lineno(), filename(), filelineno()))
440
441 if __name__ == '__main__':
442 _test()