summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamian Johnson <atagar@torproject.org>2018-08-04 17:04:24 -0700
committerDamian Johnson <atagar@torproject.org>2018-08-04 17:14:00 -0700
commit8ec17d804e7f6142fae0f14120bdf06e523e3982 (patch)
treed0d68ed3b59154b7f07472a4a4b10be87bdfc444
parentd42e9e1953ab5eac3677e16c51c1a4c28ef965ec (diff)
Better error message if file objects aren't seekable
As reported by teor, stem.descriptor's parse_file() function cannot accept stdin... https://trac.torproject.org/projects/tor/ticket/23859 The trouble is that not all file objects in python are seekable. I'd *like* to handle address this transparently by buffering the content... try: descriptor_file.tell() except IOError: # file's not seekable, wrapping in a buffer that is descriptor_file = io.BytesIO(descriptor_file.read()) This works great if our stream has content... % cat my_descriptors | python demo.py *But* hangs indefinitely if no EOF is present in the stream. % python demo.py <= hangs Turns out non-blocking, platform independent reading of streams like stdin is pretty tricky... http://eyalarubas.com/python-subproc-nonblock.html As such simply providing callers with a more descriptive exception. If they know their stream won't block *they* can add the above wrapper to provide us with a seekable file object.
-rw-r--r--stem/descriptor/__init__.py17
1 files changed, 17 insertions, 0 deletions
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 05255648..a9860140 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -93,6 +93,13 @@ __all__ = [
'Descriptor',
]
+UNSEEKABLE_MSG = """\
+File object isn't seekable. Try wrapping it with a BytesIO instead...
+
+ content = my_file.read()
+ parsed_descriptors = stem.descriptor.parse_file(io.BytesIO(content))
+"""
+
KEYWORD_CHAR = 'a-zA-Z0-9-'
WHITESPACE = ' \t'
KEYWORD_LINE = re.compile('^([%s]+)(?:[%s]+(.*))?$' % (KEYWORD_CHAR, WHITESPACE))
@@ -218,6 +225,16 @@ def parse_file(descriptor_file, descriptor_type = None, validate = False, docume
return
+ # Not all files are seekable. If unseekable then advising the user.
+ #
+ # Python 3.x adds an io.seekable() method, but not an option with python 2.x
+ # so using an experimental call to tell() to determine this.
+
+ try:
+ descriptor_file.tell()
+ except IOError:
+ raise IOError(UNSEEKABLE_MSG)
+
# The tor descriptor specifications do not provide a reliable method for
# identifying a descriptor file's type and version so we need to guess
# based on its filename. Metrics descriptors, however, can be identified