[Python-Dev] zipfile.py, file system abstractions
Fred L. Drake, Jr.
fdrake@acm.org
Thu, 3 Feb 2000 10:42:42 -0500 (EST)
--rDGr2BijA3
Content-Type: text/plain; charset=us-ascii
Content-Description: message body text
Content-Transfer-Encoding: 7bit
At one point we discussed file system abstractions on this list. Do
we want to pursue the idea? I have an implementation that's been
tested on Unix: a Filesystem instance refers to either the entire
"native" filesystem, or a subset of a filesystem (either a filesystem
object or the native filesystem), based on a "root" that's a directory
within the parent filesystem.
There'd need to be some work done to make sure it works properly on
Windows and the Mac, but I don't think that would be a lot of work.
I think this should be available as an abstraction in Python.
Implementing this on top of a ZIP/JAR file is also useful. If Jim
A.'s zipfile.py will be added to the standard library in 1.6, I'd like
to add a Filesystem class that operates on a zipfile object.
Any thoughts?
If you want this, someone needs to check in and document
zipfile.py. ;) I'll add filesys.py & it's documentation.
-Fred
--
Fred L. Drake, Jr. <fdrake at acm.org>
Corporation for National Research Initiatives
--rDGr2BijA3
Content-Type: text/x-python
Content-Description: Filesystem abstraction for Python
Content-Disposition: inline;
filename="filesys.py"
Content-Transfer-Encoding: 7bit
"""FilesystemAPI.Filesystem implementations.
These aren't actually connected to ILU in any way.
"""
__version__ = '$Revision: 1.7 $'
# This probably doesn't work anywhere that doesn't use Unix-style
# pathnames.
class Filesystem:
def __init__(self, fs=None, root=None):
if fs:
self._fs = fs
self._path = fs.path
self._open = fs.open
else:
import os, __builtin__
self._fs = os
self._path = os.path
self._open = __builtin__.open
self._cwd = self._fs.sep
self.pardir = self._fs.pardir
self.curdir = self._fs.curdir
self.sep = self._fs.sep
if root:
self._root = root = self._path.abspath(root)
self.path = SubsettingPath(self, self._path, root)
else:
self._root = ''
self.path = SimplePath(self, self._path)
def __getinitargs__(self):
return (self._fs, self._root)
def chdir(self, path):
return self.__pathcall(self._fs.chdir, path)
def fstat(self, file):
try:
return self._fs.fstat(file)
except IOError, e:
# make sure no filename information leaks out
if hasattr(file, "name"):
e.filename = file.name
else:
e.filename = None
raise e
def getcwd(self):
return self._cwd
def listdir(self, path):
return self.__pathcall(self._fs.listdir, path)
def lstat(self, path):
return self.__pathcall(self._fs.lstat, path)
def mkdir(self, path):
self.__pathcall(self._fs.mkdir, path)
def fopen(self, path, mode="r"):
name = self.path._normpath(path)
try:
f = self._open(self._root + name, mode)
except IOError, e:
e.filename = path
raise e
if self._root:
f = SubsettingFile(f, path, mode)
return f
def remove(self, path):
return self.__pathcall(self._fs.remove, path)
def rmdir(self, path):
return self.__pathcall(self._fs.rmdir, path)
def stat(self, path):
return self.__pathcall(self._fs.stat, path)
def unlink(self, path):
return self.__pathcall(self._fs.unlink, path)
def __pathcall(self, func, path):
name = self.path._normpath(path)
try:
return func(self._root + name)
except IOError, e:
e.filename = path
raise e
class SubsettingFile:
"""Class used to mask a real file when the origin filesystem is being
'subsetted'.
This avoids revealing the real file name without restricting the
ability to use the file as a replacement sys.stdout (which needs
softspace support).
"""
def __init__(self, file, path, mode):
self.__dict__['_file'] = file
self.__dict__['_mode'] = mode
self.__dict__['_path'] = path
self.__dict__['name'] = path
def __getattr__(self, name):
v = getattr(self._file, name)
if callable(v) and not self.__dict__.has_key(name):
v = SubsettingMethod(v, self._path).call
self.__dict__[name] = v
return v
def __setattr__(self, name, value):
setattr(self._file, name, value)
def __repr__(self):
oc = self._file.closed and "closed" or "open"
return "<%s file %s, mode %s at %x>" \
% (oc, `self._path`, `self._mode`, id(self))
class SubsettingMethod:
def __init__(self, method, path):
self.method = method
self.path = path
def call(self, *args, **kw):
try:
return apply(self.method, args, kw)
except IOError, e:
e.filename = self.path
raise e
class BasePath:
def __init__(self, fs, realpath):
self._fs = fs
self._path = realpath
self._prefix = ''
try:
fs.stat(fs.curdir)
self._have_stat = 1
except AttributeError:
self._have_stat = 0
def __getattr__(self, name):
v = getattr(self._path, name)
setattr(self, name, v)
return v
def abspath(self, path):
if not self.isabs(path):
path = self._path.join(self._fs.getcwd(), path)
return self.normpath(path)
def basename(self, path):
return self._path.basename(path)
def commonprefix(self, list):
return self._path.commonprefix(list)
def dirname(self, path):
return self._path.dirname(path)
def exists(self, path):
name = self._normpath(path)
try:
return self._path.exists(self._prefix + name)
except IOError, e:
e.filename = path
raise e
def isabs(self, path):
return self._path.isabs(path)
def isdir(self, path):
name = self._normpath(path)
try:
return self._path.isdir(self._prefix + name)
except IOError, e:
e.filename = name
raise e
def isfile(self, path):
name = self._normpath(path)
try:
return self._path.isfile(self._prefix + name)
except IOError, e:
e.filename = path
raise e
def join(self, *args):
return apply(self._path.join, args)
def normcase(self, path):
return self._path.normcase(path)
def normpath(self, path):
return self._path.normpath(path)
def samefile(self, path1, path2):
"""Return true if both pathname arguments refer to the same
file or directory.
If the underlying filesystem supports stat(), the comparison
is made using samestat(), otherwise a simple string comparison
is used.
"""
if self._have_stat:
return self.samestat(self._fs.stat(path1), self._fs.stat(path2))
else:
p1 = self._normpath(path1)
p2 = self._normpath(path2)
return p1 == p2
def sameopenfile(self, file1, file2):
stat1 = self._fs.fstat(file1)
stat2 = self._fs.fstat(file2)
return self.samestat(stat1, stat2)
def split(self, path):
return self._path.split(path)
def splitdrive(self, path):
return self._path.splitdrive(path)
def splitext(self, path):
return self._path.splitext(path)
def walk(self, root, func, arg):
queue = [root]
while queue:
dir = queue[0]
del queue[0]
if self.isdir(dir):
files = self._fs.listdir(dir)
func(arg, dir, files)
for file in files:
path = self.join(dir, file)
if self.isdir(path):
queue.append(path)
class SimplePath(BasePath):
def __getinitargs__(self):
return (self._fs, self._path)
def _normpath(self, path):
return self.abspath(path)
class SubsettingPath(BasePath):
"""os.path equivalent that pretends some directory down in the
tree is the root.
This class works relative to a filesystem and path-math object that
are passed to the constructor.
"""
def __init__(self, fs, realpath, prefix):
BasePath.__init__(self, fs, realpath)
self._prefix = prefix
self._rootref = "%s%s%s" \
% (self._fs.sep, self._fs.pardir, self._fs.sep)
def __getinitargs__(self):
return (self._fs, self._path, self._prefix)
def _normpath(self, path):
"""Return the absolute pathname for path, relative to the
filesystem object."""
p = self.abspath(path)
while p[:len(self._rootref)] == self._rootref:
# on Unix, /.. refers to /, on Windows, \.. refers to \ (yech!)
p = p[len(self._rootref) - len(self._fs.sep):]
if not self.isabs(p):
raise IOError(errno.ENOENT, "No such file or directory", path)
return p
--rDGr2BijA3--