| # Copyright 2013 Google Inc. |
| # Copyright 2011, Nexenta Systems Inc. |
| # |
| # Permission is hereby granted, free of charge, to any person obtaining a |
| # copy of this software and associated documentation files (the |
| # "Software"), to deal in the Software without restriction, including |
| # without limitation the rights to use, copy, modify, merge, publish, dis- |
| # tribute, sublicense, and/or sell copies of the Software, and to permit |
| # persons to whom the Software is furnished to do so, subject to the fol- |
| # lowing conditions: |
| # |
| # The above copyright notice and this permission notice shall be included |
| # in all copies or substantial portions of the Software. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| # IN THE SOFTWARE. |
| |
| """ |
| Wrapper class to expose a Key being read via a partial implementaiton of the |
| Python file interface. The only functions supported are those needed for seeking |
| in a Key open for reading. |
| """ |
| |
| import os |
| from boto.exception import StorageResponseError |
| |
| class KeyFile(): |
| |
| def __init__(self, key): |
| self.key = key |
| self.key.open_read() |
| self.location = 0 |
| self.closed = False |
| self.softspace = -1 # Not implemented. |
| self.mode = 'r' |
| self.encoding = 'Undefined in KeyFile' |
| self.errors = 'Undefined in KeyFile' |
| self.newlines = 'Undefined in KeyFile' |
| self.name = key.name |
| |
| def tell(self): |
| if self.location is None: |
| raise ValueError("I/O operation on closed file") |
| return self.location |
| |
| def seek(self, pos, whence=os.SEEK_SET): |
| self.key.close(fast=True) |
| if whence == os.SEEK_END: |
| # We need special handling for this case because sending an HTTP range GET |
| # with EOF for the range start would cause an invalid range error. Instead |
| # we position to one before EOF (plus pos) and then read one byte to |
| # position at EOF. |
| if self.key.size == 0: |
| # Don't try to seek with an empty key. |
| return |
| pos = self.key.size + pos - 1 |
| if pos < 0: |
| raise IOError("Invalid argument") |
| self.key.open_read(headers={"Range": "bytes=%d-" % pos}) |
| self.key.read(1) |
| self.location = pos + 1 |
| return |
| |
| if whence == os.SEEK_SET: |
| if pos < 0: |
| raise IOError("Invalid argument") |
| elif whence == os.SEEK_CUR: |
| pos += self.location |
| else: |
| raise IOError('Invalid whence param (%d) passed to seek' % whence) |
| try: |
| self.key.open_read(headers={"Range": "bytes=%d-" % pos}) |
| except StorageResponseError as e: |
| # 416 Invalid Range means that the given starting byte was past the end |
| # of file. We catch this because the Python file interface allows silently |
| # seeking past the end of the file. |
| if e.status != 416: |
| raise |
| |
| self.location = pos |
| |
| def read(self, size): |
| self.location += size |
| return self.key.read(size) |
| |
| def close(self): |
| self.key.close() |
| self.location = None |
| self.closed = True |
| |
| def isatty(self): |
| return False |
| |
| # Non-file interface, useful for code that wants to dig into underlying Key |
| # state. |
| def getkey(self): |
| return self.key |
| |
| # Unimplemented interfaces below here. |
| |
| def write(self, buf): |
| raise NotImplementedError('write not implemented in KeyFile') |
| |
| def fileno(self): |
| raise NotImplementedError('fileno not implemented in KeyFile') |
| |
| def flush(self): |
| raise NotImplementedError('flush not implemented in KeyFile') |
| |
| def next(self): |
| raise NotImplementedError('next not implemented in KeyFile') |
| |
| def readinto(self): |
| raise NotImplementedError('readinto not implemented in KeyFile') |
| |
| def readline(self): |
| raise NotImplementedError('readline not implemented in KeyFile') |
| |
| def readlines(self): |
| raise NotImplementedError('readlines not implemented in KeyFile') |
| |
| def truncate(self): |
| raise NotImplementedError('truncate not implemented in KeyFile') |
| |
| def writelines(self): |
| raise NotImplementedError('writelines not implemented in KeyFile') |
| |
| def xreadlines(self): |
| raise NotImplementedError('xreadlines not implemented in KeyFile') |