keyfile.py 4.32 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
# 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')