|
|
@@ -0,0 +1,237 @@
|
|
|
+""" This module reimplements Python's file I/O mechanisms using Panda
|
|
|
+constructs. This enables Python to interface more easily with Panda's
|
|
|
+virtual file system, and it also better-supports Panda's
|
|
|
+SIMPLE_THREADS model, by avoiding blocking all threads while waiting
|
|
|
+for I/O to complete. """
|
|
|
+
|
|
|
+__all__ = [
|
|
|
+ 'file', 'open',
|
|
|
+ ]
|
|
|
+
|
|
|
+from pandac import PandaModules as pm
|
|
|
+import types
|
|
|
+
|
|
|
+_vfs = pm.VirtualFileSystem.getGlobalPtr()
|
|
|
+
|
|
|
+class file:
|
|
|
+ def __init__(self, filename, mode = 'r', bufsize = None,
|
|
|
+ autoUnwrap = False):
|
|
|
+ self.__stream = None
|
|
|
+ self.__needsVfsClose = False
|
|
|
+ self.__reader = None
|
|
|
+ self.__writer = None
|
|
|
+ self.closed = True
|
|
|
+ self.encoding = None
|
|
|
+ self.errors = None
|
|
|
+ self.__lastWrite = False
|
|
|
+
|
|
|
+ self.mode = mode
|
|
|
+ self.name = None
|
|
|
+ self.filename = None
|
|
|
+ self.newlines = None
|
|
|
+ self.softspace = False
|
|
|
+
|
|
|
+ readMode = False
|
|
|
+ writeMode = False
|
|
|
+
|
|
|
+ if isinstance(filename, pm.Istream) or isinstance(filename, pm.Ostream):
|
|
|
+ # If we were given a stream instead of a filename, assign
|
|
|
+ # it directly.
|
|
|
+ self.__stream = filename
|
|
|
+ readMode = isinstance(filename, pm.Istream)
|
|
|
+ writeMode = isinstance(filename, pm.Ostream)
|
|
|
+
|
|
|
+ else:
|
|
|
+ # Otherwise, we must have been given a filename. Open it.
|
|
|
+ if isinstance(filename, types.StringTypes):
|
|
|
+ # If a raw string is given, assume it's an os-specific
|
|
|
+ # filename.
|
|
|
+ filename = pm.Filename.fromOsSpecific(filename)
|
|
|
+ else:
|
|
|
+ # If a Filename is given, make a writable copy anyway.
|
|
|
+ filename = pm.Filename(filename)
|
|
|
+
|
|
|
+ self.filename = filename
|
|
|
+ self.name = filename.toOsSpecific()
|
|
|
+
|
|
|
+ binary = False
|
|
|
+ if 'b' in mode:
|
|
|
+ # Strip 'b'. This means a binary file.
|
|
|
+ mode = mode[:i] + mode[i + 1:]
|
|
|
+ binary = True
|
|
|
+
|
|
|
+ if 'U' in mode:
|
|
|
+ # Strip 'U'. We don't use it; universal-newline support
|
|
|
+ # is built into Panda, and can't be changed at runtime.
|
|
|
+ i = mode.index('U')
|
|
|
+ mode = mode[:i] + mode[i + 1:]
|
|
|
+ binary = False
|
|
|
+
|
|
|
+ if mode == '':
|
|
|
+ mode = 'r'
|
|
|
+
|
|
|
+ # Per Python docs, we insist this is true.
|
|
|
+ assert mode[0] in 'rwa'
|
|
|
+
|
|
|
+ if binary:
|
|
|
+ filename.setBinary()
|
|
|
+ else:
|
|
|
+ filename.setText()
|
|
|
+
|
|
|
+ # Actually open the streams.
|
|
|
+ if mode == 'w':
|
|
|
+ self.__stream = pm.Pofstream()
|
|
|
+ if not filename.openWrite(self.__stream, True):
|
|
|
+ raise IOError
|
|
|
+ writeMode = True
|
|
|
+
|
|
|
+ elif mode == 'a':
|
|
|
+ self.__stream = pm.Pofstream()
|
|
|
+ if not filename.openAppend(self.__stream):
|
|
|
+ raise IOError
|
|
|
+ writeMode = True
|
|
|
+
|
|
|
+ elif mode == 'w+':
|
|
|
+ self.__stream = pm.Pfstream()
|
|
|
+ if not filename.openReadWrite(self.__stream, True):
|
|
|
+ raise IOError
|
|
|
+ readMode = True
|
|
|
+ writeMode = True
|
|
|
+
|
|
|
+ elif mode == 'a+':
|
|
|
+ self.__stream = pm.Pfstream()
|
|
|
+ if not filename.openReadAppend(self.__stream):
|
|
|
+ raise IOError
|
|
|
+ readMode = True
|
|
|
+ writeMode = True
|
|
|
+
|
|
|
+ elif mode == 'r+':
|
|
|
+ self.__stream = pm.Pfstream()
|
|
|
+ if not filename.exists():
|
|
|
+ raise IOError
|
|
|
+ if not filename.openReadWrite(self.__stream, False):
|
|
|
+ raise IOError
|
|
|
+ readMode = True
|
|
|
+ writeMode = True
|
|
|
+
|
|
|
+ elif mode == 'r':
|
|
|
+ # For strictly read mode (not write or read-write
|
|
|
+ # mode), we use Panda's VirtualFileSystem.
|
|
|
+
|
|
|
+ self.__stream = _vfs.openReadFile(filename, autoUnwrap)
|
|
|
+ if not self.__stream:
|
|
|
+ raise IOError
|
|
|
+ self.__needsVfsClose = True
|
|
|
+ readMode = True
|
|
|
+
|
|
|
+ if readMode:
|
|
|
+ self.__reader = pm.StreamReader(self.__stream, False)
|
|
|
+ if writeMode:
|
|
|
+ self.__writer = pm.StreamWriter(self.__stream, False)
|
|
|
+
|
|
|
+ def __del__(self):
|
|
|
+ self.close()
|
|
|
+
|
|
|
+ def close(self):
|
|
|
+ if self.__needsVfsClose:
|
|
|
+ _vfs.closeReadFile(self.__stream)
|
|
|
+ self.__needsVfsClose = False
|
|
|
+ self.__stream = None
|
|
|
+ self.__needsVfsClose = False
|
|
|
+ self.__reader = None
|
|
|
+ self.__writer = None
|
|
|
+
|
|
|
+ def flush(self):
|
|
|
+ if self.__stream:
|
|
|
+ self.__stream.flush()
|
|
|
+
|
|
|
+ def __iter__(self):
|
|
|
+ return self
|
|
|
+
|
|
|
+ def next(self):
|
|
|
+ line = self.readline()
|
|
|
+ if line:
|
|
|
+ return line
|
|
|
+ raise StopIteration
|
|
|
+
|
|
|
+ def read(self, size = -1):
|
|
|
+ if not self.__reader:
|
|
|
+ if not self.__writer:
|
|
|
+ # The stream is not even open at all.
|
|
|
+ raise ValueError
|
|
|
+ # The stream is open only in write mode.
|
|
|
+ raise IOError
|
|
|
+
|
|
|
+ self.__lastWrite = False
|
|
|
+ if size >= 0:
|
|
|
+ return self.__reader.extractBytes(size)
|
|
|
+ else:
|
|
|
+ # Read to end-of-file.
|
|
|
+ result = ''
|
|
|
+ while not self.__stream.eof():
|
|
|
+ result += self.__reader.extractBytes(1024)
|
|
|
+ return result
|
|
|
+
|
|
|
+ def readline(self, size = -1):
|
|
|
+ if not self.__reader:
|
|
|
+ if not self.__writer:
|
|
|
+ # The stream is not even open at all.
|
|
|
+ raise ValueError
|
|
|
+ # The stream is open only in write mode.
|
|
|
+ raise IOError
|
|
|
+
|
|
|
+ self.__lastWrite = False
|
|
|
+ return self.__reader.readline()
|
|
|
+
|
|
|
+ def readlines(self, sizehint = -1):
|
|
|
+ lines = []
|
|
|
+ line = self.readline()
|
|
|
+ while line:
|
|
|
+ lines.append(line)
|
|
|
+ line = self.readline()
|
|
|
+ return lines
|
|
|
+
|
|
|
+ xreadlines = readlines
|
|
|
+
|
|
|
+ def seek(self, offset, whence = 0):
|
|
|
+ if self.__reader:
|
|
|
+ self.__stream.seekg(offset, whence)
|
|
|
+ if self.__writer:
|
|
|
+ self.__stream.seekp(offset, whence)
|
|
|
+
|
|
|
+ def tell(self):
|
|
|
+ if self.__lastWrite:
|
|
|
+ if self.__writer:
|
|
|
+ return self.__stream.tellp()
|
|
|
+ else:
|
|
|
+ if self.__reader:
|
|
|
+ return self.__stream.tellg()
|
|
|
+ raise ValueError
|
|
|
+
|
|
|
+ def truncate(self):
|
|
|
+ """ Sorry, this isn't supported by Panda's low-level I/O,
|
|
|
+ because it isn't supported by the standard C++ library. """
|
|
|
+ raise NotImplementedError
|
|
|
+
|
|
|
+ def write(self, str):
|
|
|
+ if not self.__writer:
|
|
|
+ if not self.__reader:
|
|
|
+ # The stream is not even open at all.
|
|
|
+ raise ValueError
|
|
|
+ # The stream is open only in read mode.
|
|
|
+ raise IOError
|
|
|
+ self.__writer.appendData(str)
|
|
|
+ self.__lastWrite = True
|
|
|
+
|
|
|
+ def writelines(self, lines):
|
|
|
+ if not self.__writer:
|
|
|
+ if not self.__reader:
|
|
|
+ # The stream is not even open at all.
|
|
|
+ raise ValueError
|
|
|
+ # The stream is open only in read mode.
|
|
|
+ raise IOError
|
|
|
+ for line in lines:
|
|
|
+ self.__writer.appendData(line)
|
|
|
+ self.__lastWrite = True
|
|
|
+
|
|
|
+open = file
|