syntactic sugar for filesystem access in Python

Kragen Sitaker kragen at dnaco.net
Wed Sep 27 01:19:59 CEST 2000


It is with some trepidation that I post this; I'm not sure it's
useful.  It provides some very simple filesystem-access functionality
in a very simple way, but it will throw exceptions at surprising times
if the filesystem changes underneath it.

Still, it should be entertaining.

>From kragen at kirk.dnaco.net Tue Sep 26 19:20:04 2000
Date: Tue, 26 Sep 2000 19:14:21 -0400 (EDT)
From: Kragen <kragen at kirk.dnaco.net>
To: kragen-hacks at kragen.dnaco.net
Subject: syntactic sugar for filesystem access in Python

Here's a sample session; source code follows:
[kragen at kragen devel]$ python
Python 1.5.2 (#1, Sep 17 1999, 20:15:36)  [GCC egcs-2.91.66 19990314/Linux (egcs- on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import filesystem
>>> fs = filesystem.Filesystem()
>>> home = fs.home.kragen
>>> nonexistent = fs.nonexistent_name
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "filesystem.py", line 21, in __getattr__
    raise AttributeError, name
AttributeError: nonexistent_name
>>> fs.bin.cat("/home/kragen/imptest.py")
class Bound:
        def __import__(*args):
                print "importing ", args
0
>>> imptest = str(home["imptest.py"])
>>> print home["imptest.py"]
class Bound:
        def __import__(*args):
                print "importing ", args

>>> fs.bin.ls("-l", "/home/kragen/.ncftp")
total 128
-rw-------   1 kragen   kragen       3958 Jul 17 17:29 firewall
-rw-------   1 kragen   kragen        870 Sep 25 00:11 history
-rw-rw-r--   1 kragen   kragen       3093 Sep 25 00:11 log
-rw-r--r--   1 kragen   kragen        415 Sep 20 13:22 prefs
-rw-------   1 kragen   kragen       3261 Sep 25 00:11 trace
-rw-------   1 kragen   kragen       3545 Jul 25 00:32 trace.002455
-rw-------   1 kragen   kragen       4465 Jul 25 15:55 trace.004094
-rw-------   1 kragen   kragen      12074 Jul 29 21:28 trace.004098
-rw-------   1 kragen   kragen       5610 Jul 25 16:35 trace.004938
-rw-------   1 kragen   kragen       3741 Aug  9 11:33 trace.010544
-rw-------   1 kragen   kragen      31031 Aug  6 13:04 trace.013201
-rw-------   1 kragen   kragen       8652 Aug  4 21:04 trace.013235
-rw-------   1 kragen   kragen       4733 Aug  4 21:13 trace.013247
-rw-------   1 kragen   kragen      10140 Aug  4 22:21 trace.013447
-rw-------   1 kragen   kragen       1421 Sep 22 19:40 trace.019487
-rw-rw-r--   1 kragen   kragen        143 Sep 25 00:11 v3init
0
>>> home[".ncftp"][:]
[Filesystem('/home/kragen/.ncftp/v3init'), Filesystem('/home/kragen/.ncftp/firewall'), Filesystem('/home/kragen/.ncftp/trace.002455'), Filesystem('/home/kragen/.ncftp/log'), Filesystem('/home/kragen/.ncftp/history'), Filesystem('/home/kragen/.ncftp/trace'), Filesystem('/home/kragen/.ncftp/prefs'), Filesystem('/home/kragen/.ncftp/trace.004094'), Filesystem('/home/kragen/.ncftp/trace.004938'), Filesystem('/home/kragen/.ncftp/trace.004098'), Filesystem('/home/kragen/.ncftp/trace.013201'), Filesystem('/home/kragen/.ncftp/trace.013235'), Filesystem('/home/kragen/.ncftp/trace.013247'), Filesystem('/home/kragen/.ncftp/trace.013447'), Filesystem('/home/kragen/.ncftp/trace.010544'), Filesystem('/home/kragen/.ncftp/trace.019487')]
>>> home[".ncftp"][0]
Filesystem('/home/kragen/.ncftp/v3init')
>>> home[".ncftp"][1]
Filesystem('/home/kragen/.ncftp/firewall')
>>> del home["imptest.py"]
>>> fs.bin.ls("/home/kragen/imptest.py")
/bin/ls: /home/kragen/imptest.py: No such file or directory
256
>>> imptest
'class Bound:\012        def __import__(*args):\012                print "importing ", args\012'
>>> home["imptest.py"] = imptest
>>> fs.bin.ls("/home/kragen/imptest.py")
/home/kragen/imptest.py
0
>>> len(str(home.core))
4808704
>>> del home.core
>>> home.core
Traceback (innermost last):
  File "<stdin>", line 1, in ?
  File "filesystem.py", line 21, in __getattr__
    raise AttributeError, name
AttributeError: core
>>> home.name()
'/home/kragen'
>>> home.dirname()
'/home'
>>> home.basename()
'kragen'
>>> 

Pretty cool, huh?  You can read and write files from the filesystem and
run external programs more conveniently in Python than in the shell this
way.  Here's the glue code that does the impedance matching between the
filesystem and Python:

# Import the filesystem into your Python programs.  Inspired by some
# comments by Mike Dierken I didn't completely understand at the time.

# I hereby disclaim any copyright interest in this work, my intent in
# so doing being to thereby put it in the public domain.
# Kragen Sitaker <kragen at pobox.com>, 2000-09-26

# There are some correctness problems with this code, having largely to do
# with things changing out from under you: the current working directory,
# These are not issues unique to Python, but they aren't there with normal
# Python code that doesn't expect to deal with a filesystem.

import os, os.path, types, string

class Filesystem:
    def __init__(self, pathname='/'):
        self.__dict__["pathname"] = pathname
    def __getattr__(self, name):
        pathname = os.path.join(self.pathname, name)
        if not os.path.exists(pathname):
            raise AttributeError, name
        return Filesystem(pathname)
    def __len__(self):
        return len(os.listdir(self.pathname))
    def __getslice__(self, start, end):
        return map ((lambda x, self=self: self.__getattr__(x)),
                    os.listdir(self.pathname))
    def __getitem__(self, key):
        if not os.path.isdir(self.pathname):
            raise TypeError, "not a directory"
        if type(key) is types.IntType:
            return self.__getattr__(os.listdir(self.pathname)[key])
        else:
            return self.__getattr__(key)
    def __delattr__(self, key):
        pathname = os.path.join(self.pathname, key)
        if os.path.isdir(pathname):
            os.rmdir(pathname)
        elif os.path.exists(pathname):
            os.remove(pathname)
        else:
            # the equivalent to this for ordinary Python objects is not
            # correct English, so I deviate for correctness
            raise AttributeError, "delete nonexistent file"
    def __delitem__(self, name):
        # sorry, we don't support deletion by numeric index :)
        return self.__delattr__(name)
    def __str__(self):
        fh = open(self.pathname, 'r')
        str = fh.read()
        fh.close()
        return str
    # XXX this is inexact; What if
    # Filesystem isn't defined in the namespace where someone tries to
    # eval this?
    def __repr__(self):
        return 'Filesystem(%s)' % repr(self.pathname)
    def name(self):
        return self.pathname
    def dirname(self):
        return os.path.split(self.pathname)[0]
    def basename(self):
        return os.path.split(self.pathname)[1]
    def __call__(self, *args):  # this could be improved!
        nargs = list(args)
        nargs.insert(0, self.name())
        return os.system(string.join(nargs))
    def __setattr__(self, name, value): # we unfortunately only support strings
        fh = open(os.path.join(self.pathname, name), 'w')
        fh.write(value)
        fh.close()
    def __setitem__(self, name, value):
        return self.__setattr__(name, value)


-- 
<kragen at pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Perilous to all of us are the devices of an art deeper than we ourselves
possess.
                -- Gandalf the Grey [J.R.R. Tolkien, "Lord of the Rings"]



More information about the Python-list mailing list