diff --git a/direct/src/stdpy/file.py b/direct/src/stdpy/file.py new file mode 100644 index 0000000000..b911fbba84 --- /dev/null +++ b/direct/src/stdpy/file.py @@ -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