diff --git a/src/FoFiX.py b/src/FoFiX.py index 71206e9f0..fe573e006 100644 --- a/src/FoFiX.py +++ b/src/FoFiX.py @@ -23,8 +23,9 @@ # MA 02110-1301, USA. # ##################################################################### -##@package FoFiX -# Main game executable. +''' +Main game executable. +''' # Register the latin-1 encoding import codecs @@ -44,10 +45,6 @@ import os import Version -## Display command-line usage and exit. -# Outputs to stdout unless py2exe'd, in which case the usage is presented -# using a MessageBox(). -# @param errmsg Optional error message. def _usage(errmsg=None): usage = """Usage: %(prog)s [options] @@ -117,7 +114,6 @@ def _usage(errmsg=None): except: videoAvailable = False -## Main function. def main(): playing = None configFile = None diff --git a/src/Log.py b/src/Log.py index 1d7a4c9bd..418b9302d 100644 --- a/src/Log.py +++ b/src/Log.py @@ -21,8 +21,9 @@ # MA 02110-1301, USA. # ##################################################################### -##@package Log -# Functions for various types of logging that FoFiX needs to do. +''' +Functions for writing to the logfile. +''' import sys import os @@ -32,10 +33,10 @@ import time import warnings -## Whether to output log entries to stdout in addition to the logfile. +# Whether to output log entries to stdout in addition to the logfile. quiet = True -## File object representing the logfile. +# File object representing the logfile. if os.name == "posix": # evilynux - logfile in ~/.fofix/ for GNU/Linux and MacOS X # evilynux - Under MacOS X, put the logs in ~/Library/Logs if os.uname()[0] == "Darwin": @@ -47,13 +48,13 @@ else: logFile = open(Version.PROGRAM_UNIXSTYLE_NAME + ".log", "w") #MFH - local logfile! -## Character encoding to use for logging. +# Character encoding to use for logging. encoding = "iso-8859-1" if "-v" in sys.argv or "--verbose" in sys.argv: quiet = False -## Labels for different priorities, as output to the logfile. +# Labels for different priorities, as output to the logfile. labels = { "warn": "(W)", "debug": "(D)", @@ -61,7 +62,7 @@ "error": "(E)", } -## Labels for different priorities, as output to stdout. +# Labels for different priorities, as output to stdout. if os.name == "posix": displaylabels = { "warn": "\033[1;33m(W)\033[0m", @@ -72,10 +73,12 @@ else: displaylabels = labels -## Generic logging function. -# @param cls Priority class for the message -# @param msg Log message text def _log(cls, msg): + ''' + Generic logging function. + @param cls: Priority class for the message + @param msg: Log message text + ''' if not isinstance(msg, unicode): msg = unicode(msg, encoding).encode(encoding, "ignore") timeprefix = "[%12.6f] " % (time.time() - _initTime) @@ -84,34 +87,42 @@ def _log(cls, msg): print >>logFile, timeprefix + labels[cls] + " " + msg logFile.flush() #stump: truncated logfiles be gone! -## Log a major error. -# If this is called while handling an exception, the traceback will -# be automatically included in the log. -# @param msg Error message text def error(msg): + ''' + Log a major error. + If this is called while handling an exception, the traceback will + be automatically included in the log. + @param msg: Error message text + ''' if sys.exc_info() == (None, None, None): #warnings.warn("Log.error() called without an active exception", UserWarning, 2) #stump: should we enforce this? _log("error", msg) else: _log("error", msg + "\n" + traceback.format_exc()) -## Log a warning. -# @param msg Warning message text def warn(msg): + ''' + Log a warning. + @param msg: Warning message text + ''' _log("warn", msg) -## Log a notice. -# @param msg Notice message text def notice(msg): + ''' + Log a notice. + @param msg: Notice message text + ''' _log("notice", msg) -## Log a debug message. -# @param msg Debug message text def debug(msg): + ''' + Log a debug message. + @param msg: Debug message text + ''' _log("debug", msg) -## A hook to catch Python warnings. def _showwarning(*args, **kw): + '''A hook to catch Python warnings.''' warn("A Python warning was issued:\n" + warnings.formatwarning(*args, **kw)) _old_showwarning(*args, **kw) _old_showwarning = warnings.showwarning diff --git a/src/Player.py b/src/Player.py index 8dc217f64..a5c421892 100644 --- a/src/Player.py +++ b/src/Player.py @@ -225,12 +225,13 @@ def sortOptionsByKey(dict): controlpath = '/users/controllers' playerpath = '/users/players' -## Turn a controller name into a virtual path to the appropriate ini. + def _makeControllerIniName(name): + '''Turn a controller name into a virtual path to the appropriate ini.''' return '%s/%s.ini' % (controlpath, name) -## Turn a player name into a virtual path to the appropriate ini. def _makePlayerIniName(name): + '''Turn a player name into a virtual path to the appropriate ini.''' return '%s/%s.ini' % (playerpath, name) control0 = None diff --git a/src/Song.py b/src/Song.py index 2933117a3..3c1ec2d9d 100644 --- a/src/Song.py +++ b/src/Song.py @@ -3559,8 +3559,9 @@ def text(self, text): text = "" -## Stores a list of songs to be played. class SongQueue: + '''Stores a list of songs to be played.''' + def __init__(self): self.songName = [] self.library = [] diff --git a/src/VFS.py b/src/VFS.py index 85644234d..0045bf6cf 100644 --- a/src/VFS.py +++ b/src/VFS.py @@ -20,24 +20,25 @@ # MA 02110-1301, USA. # ##################################################################### -##@package VFS -# Functions providing a convenient virtual filesystem. -# -# Among other things, this is how themes and mods will be implemented. -# Note that *all* VFS functions use slash-delimited paths, relieving -# other code of the need to os.path.join(). All VFS paths must also -# be absolute (i.e. start with a slash) and may not contain "." or "..". -# -# Files or directories may be mounted in the root of the VFS read-only -# or read-write. Mounting multiple things onto the same mountpoint -# overlays the newer mount's contents on the old mount's contents, but -# read-write mounts always prevail over read-only mounts in the resolution -# order. All write attempts to a given mountpoint go to the most recent -# read-write mount on that mountpoint; trying to write to a mountpoint -# that has no writable mounts raises OSError(EROFS). Modification of -# files existing in lower layers but not the most recent writable mount -# uses copy-on-write semantics. There is no way to make something in -# a lower layer appear to have been deleted, however. +''' +Functions providing a convenient virtual filesystem. + +Among other things, this is how themes and mods will be implemented. +Note that B{all} VFS functions use slash-delimited paths, relieving +other code of the need to C{os.path.join()}. All VFS paths must also +be absolute (i.e. start with a slash) and may not contain "." or "..". + +Files or directories may be mounted in the root of the VFS read-only +or read-write. Mounting multiple things onto the same mountpoint +overlays the newer mount's contents on the old mount's contents, but +read-write mounts always prevail over read-only mounts in the resolution +order. All write attempts to a given mountpoint go to the most recent +read-write mount on that mountpoint; trying to write to a mountpoint +that has no writable mounts raises C{OSError(EROFS)}. Modification of +files existing in lower layers but not the most recent writable mount +uses copy-on-write semantics. There is no way to make something in +a lower layer appear to have been deleted, however. +''' import Version @@ -55,34 +56,38 @@ _mountTable = {} -## Implementation of a mount point in the VFS root. -# @private class Mount(object): + '''Implementation of a mount point in the VFS root.''' + def __init__(self): - ## List of read-only backing directories, in decreasing order of priority. + # List of read-only backing directories, in decreasing order of priority. self.readOnly = [] - ## List of writable backing directories, in decreasing order of priority. + # List of writable backing directories, in decreasing order of priority. self.writable = [] - ## Resolve a path to a file that the user wants to read. - # @param path Virtual path within this mount - # @return Physical path to the file - # @throws OSError(ENOENT) if file does not exist def resolveRead(self, path): + ''' + Resolve a path to a file that the user wants to read. + @param path: Virtual path within this mount + @return: Physical path to the file + @raise OSError(ENOENT): if file does not exist + ''' for p in (self.writable + self.readOnly): candidate = os.path.join(p, path).rstrip(os.sep) if os.path.exists(candidate): return candidate raise OSError(errno.ENOENT, os.strerror(errno.ENOENT)) - ## Resolve a path to a file that the user wants to create or modify. - # Copies files to the most recently mounted writable directory if - # necessary. If a path is returned, it is guaranteed to be in the - # most recently mounted writable directory. - # @param path Virtual path within this mount - # @return Physical path to the file - # @throws OSError(EROFS) if all mounted directories are read-only def resolveWrite(self, path): + ''' + Resolve a path to a file that the user wants to create or modify. + Copies files to the most recently mounted writable directory if + necessary. If a path is returned, it is guaranteed to be in the + most recently mounted writable directory. + @param path: Virtual path within this mount + @return: Physical path to the file + @raise OSError(EROFS): if all mounted directories are read-only + ''' if len(self.writable) == 0: raise OSError(errno.EROFS, os.strerror(errno.EROFS)) @@ -103,10 +108,12 @@ def resolveWrite(self, path): shutil.copy2(rpath, wpath) return wpath - ## List the contents of a directory within this mountpoint. - # @param path Virtual path within this mount to list - # @return List of entries (excluding '.' and '..') def listdir(self, path): + ''' + List the contents of a directory within this mountpoint. + @param path: Virtual path within this mount to list + @return: List of entries (excluding '.' and '..') + ''' contents = set() for p in (self.writable + self.readOnly): candidate = os.path.join(p, path) @@ -115,32 +122,40 @@ def listdir(self, path): return list(contents) -## Mount a directory read-only. -# The virtual directory provides the union of the contents of all -# directories mounted on it. -# @param dir Directory to mount -# @param mountpt Virtual directory to mount it onto def mount(dir, mountpt): + ''' + Mount a directory read-only. + The virtual directory provides the union of the contents of all + directories mounted on it. + @param dir: Directory to mount + @param mountpt: Virtual directory to mount it onto + ''' if mountpt not in _mountTable: _mountTable[mountpt] = Mount() _mountTable[mountpt].readOnly.append(dir) -## Mount a directory and allow writing. -# The most recent writable-mounted directory on a given -# mountpoint receives all writes made to the mountpoint. -# @param dir Directory to mount -# @param mountpt Virtual directory to mount it onto + def mountWritable(dir, mountpt): + ''' + Mount a directory and allow writing. + The most recent writable-mounted directory on a given + mountpoint receives all writes made to the mountpoint. + @param dir: Directory to mount + @param mountpt: Virtual directory to mount it onto + ''' if mountpt not in _mountTable: _mountTable[mountpt] = Mount() _mountTable[mountpt].writable.append(dir) -## Validate and convert a VFS path to a mount point and a native path -# fragment suitable for passing to the mount point's methods. -# @param path VFS path -# @return 2-tuple of mount and native path fragment to pass to the mount -# @throws OSError(EINVAL) on syntactically invalid VFS path + def _convertPath(path): + ''' + Validate and convert a VFS path to a mount point and a native path + fragment suitable for passing to the mount point's methods. + @param path: VFS path + @return: 2-tuple of mount and native path fragment to pass to the mount + @raise OSError(EINVAL): on syntactically invalid VFS path + ''' if re.match('^/[^:\\\\]*$', path) is None or re.match('/\\.\\.?(/|$)', path) is not None: raise OSError(errno.EINVAL, os.strerror(errno.EINVAL)) while '//' in path: @@ -150,34 +165,49 @@ def _convertPath(path): raise OSError(errno.ENOENT, os.strerror(errno.ENOENT)) return _mountTable[components[0]], os.sep.join(components[1:]) -## Convert a VFS path into a real path that is usable to access an -# already-existing file or directory. -# @param path VFS path -# @return Real path -# @throws OSError(ENOENT) if path does not exist + def resolveRead(path): + ''' + Convert a VFS path into a real path that is usable to access an + already-existing file or directory. + @param path: VFS path + @return: Real path + @raise OSError(ENOENT): if path does not exist + ''' mount, frag = _convertPath(path) return mount.resolveRead(frag) -## Convert a VFS path that the user wishes to write a file to into a real -# writable path. Copies a file from a read-only area to a read-write area -# if necessary. -# @param path VFS path -# @return Real path + def resolveWrite(path): + ''' + Convert a VFS path that the user wishes to write a file to into a real + writable path. Copies a file from a read-only area to a read-write area + if necessary. + @param path: VFS path + @return: Real path + ''' mount, frag = _convertPath(path) return mount.resolveWrite(frag) -## stat() result for an object in the virtual filesystem. -# This was originally a hack to give synthesized stat() results for / so -# things worked right in the test code, but it's left in because if you -# can stat anything else in the VFS, you should be able to stat / too. + class StatResult(object): + ''' + C{stat()} result for an object in the virtual filesystem. + This was originally a hack to give synthesized C{stat()} results for / so + things worked right in the test code, but it's left in because if you + can stat anything else in the VFS, you should be able to stat / too. + + For all practical purposes, this object is compatible with the return type + of C{os.stat()}. Fields of the result can be accessed either as attributes + or via numeric indices. + ''' - ## Set the object up with os.stat() results of path or - # synthesized properties if the VFS root is statted. - # @param path Path to operate on def __init__(self, path): + ''' + Set the object up with C{os.stat()} results of C{path} or + synthesized properties if the VFS root is statted. + @param path: Path to operate on + ''' self._attrs = ('st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', 'st_size', 'st_atime', 'st_mtime', 'st_ctime') if path == '/': @@ -196,63 +226,85 @@ def __init__(self, path): for a in self._attrs: setattr(self, a, getattr(s, a)) - ## Sequence protocol (os.stat() returns a tuple-like object) + # Implement the sequence protocol (os.stat() returns a tuple-like object) def __len__(self): return len(self._attrs) - ## Sequence protocol (os.stat() returns a tuple-like object) + def __getitem__(self, idx): return getattr(self, self._attrs[idx]) - ## Show our contents when repr()'d. + + # Show our contents when repr()'d. def __repr__(self): return str(tuple(self)) -## Get some properties of the specified path, much like os.stat(). -# @param path Path to operate on -# @return StatResult for the path + def stat(path): + ''' + Get some properties of the specified path, much like C{os.stat()}. + @param path: Path to operate on + @return: L{StatResult} for the path + ''' return StatResult(path) -## List the contents of a virtual directory, much like os.listdir(). -# @param path Path to list -# @return List of names of objects in the directory -# (excludes '.' and '..') + def listdir(path): + ''' + List the contents of a virtual directory, much like C{os.listdir()}. + @param path: Path to list + @return: List of names of objects in the directory + (excludes '.' and '..') + ''' if path == '/': return list(_mountTable.keys()) else: mount, frag = _convertPath(path) return mount.listdir(frag) -## Delete a virtual file. -# -# Note: If the virtual file exists in one of the read-only backing -# directories of the mount in which the file is deleted, the file -# will instead appear to revert to the read-only version. -# @param path Path to delete + def unlink(path): + ''' + Delete a virtual file. + + Note: If the virtual file exists in one of the read-only backing + directories of the mount in which the file is deleted, the file + will instead appear to revert to the read-only version. + @param path: Path to delete + ''' os.unlink(resolveWrite(path)) -## Create a virtual directory. Also creates any directories leading -# up to it that are missing (like os.makedirs()). -# @param path Path at which to create a directory + def mkdir(path): + ''' + Create a virtual directory. Also creates any directories leading + up to it that are missing (like C{os.makedirs()}). + @param path: Path at which to create a directory + ''' os.makedirs(resolveWrite(path)) -## Remove a virtual directory. The directory must be empty. -# @param path Path to directory to remove + def rmdir(path): + ''' + Remove a virtual directory. The directory must be empty. + @param path: Path to directory to remove + ''' os.rmdir(resolveWrite(path)) -## Rename or move a virtual object. -# @param src Path to rename from -# @param dest Path to rename to + def rename(src, dest): + ''' + Rename or move a virtual object. + @param src: Path to rename from + @param dest: Path to rename to + ''' os.rename(resolveWrite(src), resolveWrite(dest)) -## Check the existence of a virtual object at a given path. -# @param path Path to check for existence -# @return True if object exists, False otherwise + def exists(path): + ''' + Check the existence of a virtual object at a given path. + @param path: Path to check for existence + @return: True if object exists, False otherwise + ''' try: stat(path) return True @@ -261,10 +313,13 @@ def exists(path): return False raise -## Check whether a virtual path represents a file. -# @param path Path to check for file-ness -# @return True if it is a file, False otherwise + def isfile(path): + ''' + Check whether a virtual path represents a file. + @param path: Path to check for file-ness + @return: True if it is a file, False otherwise + ''' try: return S_ISREG(stat(path).st_mode) except OSError, e: @@ -272,10 +327,13 @@ def isfile(path): return False raise -## Check whether a virtual path represents a directory. -# @param path Path to check for dir-ness -# @return True if it is a dir, False otherwise + def isdir(path): + ''' + Check whether a virtual path represents a directory. + @param path: Path to check for dir-ness + @return: True if it is a dir, False otherwise + ''' try: return S_ISDIR(stat(path).st_mode) except OSError, e: @@ -283,21 +341,28 @@ def isdir(path): return False raise + _realopen = open -## Open a virtual file, much like the built-in open() function. -# @param path Path to open -# @param mode File mode (defaults to 'r') -# @return File object of the appropriate physical file def open(path, mode='r'): + ''' + Open a virtual file, much like the built-in C{open()} function. + @param path: Path to open + @param mode: File mode + @return: File object of the appropriate physical file + ''' if mode in ('r', 'rb'): return _realopen(resolveRead(path), mode) else: return _realopen(resolveWrite(path), mode) -## Open a virtual file as a writable SQLite database. -# @param path Path to open -# @return sqlite3.Connection object for the file + def openSqlite3(path): + ''' + Open a virtual file as a writable SQLite database. + @param path: Path to open + @return: C{sqlite3.Connection} object for the file + ''' + # There is a bug in the sqlite3 module's handling of path names containing # unicode characters, so work around that by temporarily changing directory # so we access the file with just its base name. diff --git a/src/Version.py b/src/Version.py index 896779e6e..6fad21098 100644 --- a/src/Version.py +++ b/src/Version.py @@ -62,10 +62,11 @@ def revision(): def versionNum(): return "%d.%d.%d" % (MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION) -## Are we running from a py2exe'd Windows executable? (Because typing the -# test out explicitly everywhere detracts from code readability.) -# @return boolean for whether this is the Windows executable def isWindowsExe(): + ''' + Are we running from a py2exe'd Windows executable? + @return: boolean for whether this is the Windows executable + ''' return hasattr(sys, 'frozen') and sys.frozen == 'windows_exe' # evilynux: Returns version number w.r.t. frozen state