Skip to content

Commit

Permalink
[open] handle input files that cannot seek backward
Browse files Browse the repository at this point in the history
  • Loading branch information
midichef committed Jul 28, 2023
1 parent d28bd99 commit 3c14d75
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 5 deletions.
9 changes: 6 additions & 3 deletions visidata/_open.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ def openPath(vd, p, filetype=None, create=False):
filetype = filetype.lower()

if not p.exists():
if not create:
return None
newfunc = getattr(vd, 'new_' + filetype, vd.getGlobals().get('new_' + filetype))
if not newfunc:
vd.warning('%s does not exist, creating new sheet' % p)
Expand All @@ -106,6 +104,9 @@ def openPath(vd, p, filetype=None, create=False):
openfuncname = 'open_' + filetype
openfunc = getattr(vd, openfuncname, vd.getGlobals().get(openfuncname))
if not openfunc:
if not p.seekable():
# read the file as text, into a RepeatFile that can be opened multiple times
p = Path(p.given, fp=p.open(mode='rb'))
opts = vd.guessFiletype(p)
if opts and 'filetype' in opts:
filetype = opts['filetype']
Expand All @@ -130,7 +131,6 @@ def openPath(vd, p, filetype=None, create=False):

return openfunc(p)


@VisiData.api
def openSource(vd, p, filetype=None, create=False, **kwargs):
'''Return unloaded sheet object for *p* opened as the given *filetype* and with *kwargs* as option overrides. *p* can be a Path or a string (filename, url, or "-" for stdin).
Expand Down Expand Up @@ -164,6 +164,9 @@ def openSource(vd, p, filetype=None, create=False, **kwargs):
def open_txt(vd, p):
'Create sheet from `.txt` file at Path `p`, checking whether it is TSV.'
if p.exists(): #1611
if not p.seekable():
# read the file as text, into a RepeatFile that can be opened multiple times
p = Path(p.given, fp=p.open(mode='rb'))
with p.open(encoding=vd.options.encoding) as fp:
delimiter = vd.options.delimiter
try:
Expand Down
22 changes: 20 additions & 2 deletions visidata/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ def open(self, mode='rt', encoding=None, encoding_errors=None, newline=None):
# rfile makes a single-access fp reusable

if self.rfile:
if 'b' in mode:
raise ValueError('a RepeatFile holds text and cannot be reopened in binary mode')
return self.rfile.reopen()

if self.fp:
Expand Down Expand Up @@ -291,6 +293,12 @@ def read_bytes(self):
with self.open(mode='rb') as fp:
return fp.read()

# Some unusual paths can be read from, but do not allow seeking.
# For example, a pipe created by process substitution in bash, or a symlink to a fifo.
def seekable(self):
with self.open() as fp:
return fp.seekable()

def is_url(self):
'Return True if the given path appears to be a URL.'
return '://' in self.given
Expand Down Expand Up @@ -376,9 +384,19 @@ def tell(self):
'''Tells the current position as an opaque line marker.'''
return self.iter.nextIndex

def seek(self, n):
def seek(self, offset, whence=io.SEEK_SET):
'''Seek to an already seen opaque line position marker only.'''
self.iter.nextIndex = n
if whence != io.SEEK_SET and offset != 0:
if whence == io.SEEK_CUR:
raise io.UnsupportedOperation("can't do nonzero cur-relative seeks")
elif whence == io.SEEK_END:
raise io.UnsupportedOperation("can't do nonzero end-relative seeks")
else:
raise ValueError('invalid whence (%s, should be %s, %s or %s)' % (whence, io.SEEK_SET, io.SEEK_CUR, io.SEEK_END))
self.iter.nextIndex = offset

def seekable(self):
return True

def readline(self, size=-1):
if size != -1:
Expand Down

0 comments on commit 3c14d75

Please sign in to comment.