path
Python moduleDownload path.py v1.1.2 for Python 2.2 or later.
This module provides a single class that wraps the functionality in
the os.path
module. You wouldn't think that would be so
helpful, but in practice I find it much more pleasant to write and to
read.
License: You may use path.py for whatever you wish, at your own risk. (For example, you may modify, relicense, and redistribute it.) It is provided without any guarantee or warranty of any kind, not even for merchantability or fitness for any purpose.
If you do make changes to path.py, please consider sending them along to me at jason@jorendorff.com.
There's a test suite now, although it isn't very thorough. test_path.py requires a matching version of path.py.
Why write something like this?
os.path
isn't, for me; not because
functionality is missing but because the interface is annoying.os.path.join
in about 1994.os.path.walk()
works.path
Start with from path import path
. You
can then create path
objects by calling
path('/usr/local/bin')
or
path('C:\\Program Files')
, etc.
The path
object incorporates features from several
existing Python standard library modules:
os.path
are methods of path
. So instead of
os.path.abspath(os.path.normpath(f))
, you can write
f.normpath().abspath()
. (However, note that
os.path.split(f)
becomes f.splitpath()
and
os.path.join(a, b)
becomes a.joinpath(b)
, to
avoid conflicting with the string methods split()
and
join()
.)os
module are also path
methods. For example,
os.makedirs(d)
becomes
d.makedirs()
.path.glob()
method wraps Python's glob
module. For example, mydir.glob('*.py')
returns a list of the Python files in the directory specified by
mydir
.shutil
module are all methods of path
. copy()
,
copyfile()
, copytree()
, and
rmtree()
are particularly useful.path
also has these additional features:
/
operator calls
os.path.join()
. So, build_dir / "src"
means
os.path.join(build_dir, "src")
. The result is
another path
, of course.path
object that names a
directory. The items in the path
are the files and
subdirectories in the directory—as path
objects, of course. [Footnote: Because of this,
list(mypath)
lists the contents of the directory: a
Python pun.]path
is a subclass of str
(or
unicode
, if Python supports Unicode filenames on your
OS.) This means that you can use pass a path
object
to just about any function that can use a string (for example,
os.rename()
). As a bonus, you can use string methods,
like endswith()
, and file methods, like
isdir()
, on the same object. It's terribly
convenient.path
has a few read-only properties:
parent
name
ext
os.path.splitext()
drive
os.path.splitdrive()
uncshare
(Windows only)os.path.splitunc()
path.walk()
returns an iterator which
steps recursively through a whole directory tree.
path.walkdirs()
is the same, but only yields the
directories.path.lines()
returns the lines in a file.
path.bytes()
returns the binary content of a file as
one long string. path.text()
returns the text of a
file as one long string.path.touch()
acts like the Unix
touch
command. It updates the modify time and access
time of the file; it creates an empty file if none is there.relpath()
method converts a path object into
a relative path, based off of the current working directory. It
is somewhat complementary to abspath()
. The method
p1.relpathto(p2)
returns a relative path to
p2
, starting from p1
. For example, on
Windows path('C:\\Python22').relpathto('C:\\Python22\\Lib\\path.py')
returns path('Lib\\path.py')
.Pure convenience. I find it a joy to use, and I hope you will too.
# with os.path DIR = '/usr/home/guido/bin' for f in os.listdir(DIR): path = os.path.join(DIR, f) if os.path.isfile(path) and f.endswith('.py'): os.chmod(path, 0755) # with path dir = path('/usr/home/guido/bin') for f in dir.files(): if f.ext == '.py': f.chmod(0755)
# with os.path.walk def delete_backups(arg, dirname, names): for name in names: if name.endswith('~'): os.remove(os.path.join(dirname, name)) os.path.walk(os.environ['HOME'], delete_backups, None) # with os.path, if (like me) you can never remember how os.path.walk works def walk_tree_delete_backups(dir): for name in os.listdir(dir): path = os.path.join(dir, name) if os.path.isdir(path): walk_tree_delete_backups(path) elif name.endswith('~'): os.remove(path) walk_tree_delete_backups(os.environ['HOME']) # with path dir = path(os.environ['HOME']) for f in dir.walk(): if f.isfile() and f.endswith('~'): f.remove()
for c in str
means one thing, and for c in
path
means something completely different. This is
intentional, but it breaks compatibility with str
a bit.
This means you have to be somewhat careful passing path
objects to functions that expect strings; if they try to iterate over
the characters in the string, they'll malfunction.
Another reason to be careful passing path
objects to
functions expecting strings is that a function that checks the type of
the string, with code like if type(s) is type(''): ...
will fail. This may be considered a bug in the function, though. The
test should be written if isinstance(s, (str, unicode))
,
or in Python 2.3, if isinstance(s, basestring)
, both of
which allow for string subclasses.
path.__iter__()
and
path.__contains__()
aren't really on the same
wavelength. This means that for child in path
and
if child in path
aren't using the word "in" to mean the
same thing, which is mildly annoying in principle (though not in my
experience, so far). Of course, this is true of string objects
anyway, but the discrepancy isn't as great.
os.path.join
doesn't map to
path.join()
, because there's a string method with that
name. Instead it's path.joinpath()
. This is a nuisance,
but changing the semantics of base class methods is worse. (I know, I
tried it.) The same goes for split()
.
Many thanks to Thiébaut Champenier, Gary Herron, John A. Ferguson, Lars Gustäbel, John Bridle, Jesper Hertel, Michael Urman, Oren Tirosh, Garth Kidd, Detlef Lannert, and probably several other people I have missed, for pointing out bugs and possible improvements—and providing bits of code.