dbrecio.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. """
  2. File-like objects that read from or write to a bsddb record.
  3. This implements (nearly) all stdio methods.
  4. f = DBRecIO(db, key, txn=None)
  5. f.close() # explicitly release resources held
  6. flag = f.isatty() # always false
  7. pos = f.tell() # get current position
  8. f.seek(pos) # set current position
  9. f.seek(pos, mode) # mode 0: absolute; 1: relative; 2: relative to EOF
  10. buf = f.read() # read until EOF
  11. buf = f.read(n) # read up to n bytes
  12. f.truncate([size]) # truncate file at to at most size (default: current pos)
  13. f.write(buf) # write at current position
  14. f.writelines(list) # for line in list: f.write(line)
  15. Notes:
  16. - fileno() is left unimplemented so that code which uses it triggers
  17. an exception early.
  18. - There's a simple test set (see end of this file) - not yet updated
  19. for DBRecIO.
  20. - readline() is not implemented yet.
  21. From:
  22. Itamar Shtull-Trauring <itamar@maxnm.com>
  23. """
  24. import errno
  25. import string
  26. class DBRecIO:
  27. def __init__(self, db, key, txn=None):
  28. self.db = db
  29. self.key = key
  30. self.txn = txn
  31. self.len = None
  32. self.pos = 0
  33. self.closed = 0
  34. self.softspace = 0
  35. def close(self):
  36. if not self.closed:
  37. self.closed = 1
  38. del self.db, self.txn
  39. def isatty(self):
  40. if self.closed:
  41. raise ValueError, "I/O operation on closed file"
  42. return 0
  43. def seek(self, pos, mode = 0):
  44. if self.closed:
  45. raise ValueError, "I/O operation on closed file"
  46. if mode == 1:
  47. pos = pos + self.pos
  48. elif mode == 2:
  49. pos = pos + self.len
  50. self.pos = max(0, pos)
  51. def tell(self):
  52. if self.closed:
  53. raise ValueError, "I/O operation on closed file"
  54. return self.pos
  55. def read(self, n = -1):
  56. if self.closed:
  57. raise ValueError, "I/O operation on closed file"
  58. if n < 0:
  59. newpos = self.len
  60. else:
  61. newpos = min(self.pos+n, self.len)
  62. dlen = newpos - self.pos
  63. r = self.db.get(self.key, txn=self.txn, dlen=dlen, doff=self.pos)
  64. self.pos = newpos
  65. return r
  66. __fixme = """
  67. def readline(self, length=None):
  68. if self.closed:
  69. raise ValueError, "I/O operation on closed file"
  70. if self.buflist:
  71. self.buf = self.buf + string.joinfields(self.buflist, '')
  72. self.buflist = []
  73. i = string.find(self.buf, '\n', self.pos)
  74. if i < 0:
  75. newpos = self.len
  76. else:
  77. newpos = i+1
  78. if length is not None:
  79. if self.pos + length < newpos:
  80. newpos = self.pos + length
  81. r = self.buf[self.pos:newpos]
  82. self.pos = newpos
  83. return r
  84. def readlines(self, sizehint = 0):
  85. total = 0
  86. lines = []
  87. line = self.readline()
  88. while line:
  89. lines.append(line)
  90. total += len(line)
  91. if 0 < sizehint <= total:
  92. break
  93. line = self.readline()
  94. return lines
  95. """
  96. def truncate(self, size=None):
  97. if self.closed:
  98. raise ValueError, "I/O operation on closed file"
  99. if size is None:
  100. size = self.pos
  101. elif size < 0:
  102. raise IOError(errno.EINVAL,
  103. "Negative size not allowed")
  104. elif size < self.pos:
  105. self.pos = size
  106. self.db.put(self.key, "", txn=self.txn, dlen=self.len-size, doff=size)
  107. def write(self, s):
  108. if self.closed:
  109. raise ValueError, "I/O operation on closed file"
  110. if not s: return
  111. if self.pos > self.len:
  112. self.buflist.append('\0'*(self.pos - self.len))
  113. self.len = self.pos
  114. newpos = self.pos + len(s)
  115. self.db.put(self.key, s, txn=self.txn, dlen=len(s), doff=self.pos)
  116. self.pos = newpos
  117. def writelines(self, list):
  118. self.write(string.joinfields(list, ''))
  119. def flush(self):
  120. if self.closed:
  121. raise ValueError, "I/O operation on closed file"
  122. """
  123. # A little test suite
  124. def _test():
  125. import sys
  126. if sys.argv[1:]:
  127. file = sys.argv[1]
  128. else:
  129. file = '/etc/passwd'
  130. lines = open(file, 'r').readlines()
  131. text = open(file, 'r').read()
  132. f = StringIO()
  133. for line in lines[:-2]:
  134. f.write(line)
  135. f.writelines(lines[-2:])
  136. if f.getvalue() != text:
  137. raise RuntimeError, 'write failed'
  138. length = f.tell()
  139. print 'File length =', length
  140. f.seek(len(lines[0]))
  141. f.write(lines[1])
  142. f.seek(0)
  143. print 'First line =', repr(f.readline())
  144. here = f.tell()
  145. line = f.readline()
  146. print 'Second line =', repr(line)
  147. f.seek(-len(line), 1)
  148. line2 = f.read(len(line))
  149. if line != line2:
  150. raise RuntimeError, 'bad result after seek back'
  151. f.seek(len(line2), 1)
  152. list = f.readlines()
  153. line = list[-1]
  154. f.seek(f.tell() - len(line))
  155. line2 = f.read()
  156. if line != line2:
  157. raise RuntimeError, 'bad result after seek back from EOF'
  158. print 'Read', len(list), 'more lines'
  159. print 'File length =', f.tell()
  160. if f.tell() != length:
  161. raise RuntimeError, 'bad length'
  162. f.close()
  163. if __name__ == '__main__':
  164. _test()
  165. """