diff --git a/Lib/genericpath.py b/Lib/genericpath.py new file mode 100644 index 00000000000..124bdca69c7 --- /dev/null +++ b/Lib/genericpath.py @@ -0,0 +1,78 @@ +""" +Path operations common to more than one OS +Do not use directly. The OS specific modules import the appropriate +functions from this module themselves. +""" +import os +import stat + +__all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', + 'getsize', 'isdir', 'isfile'] + + +# Does a path exist? +# This is false for dangling symbolic links on systems that support them. +def exists(path): + """Test whether a path exists. Returns False for broken symbolic links""" + try: + st = os.stat(path) + except os.error: + return False + return True + + +# This follows symbolic links, so both islink() and isdir() can be true +# for the same path ono systems that support symlinks +def isfile(path): + """Test whether a path is a regular file""" + try: + st = os.stat(path) + except os.error: + return False + return stat.S_ISREG(st.st_mode) + + +# Is a path a directory? +# This follows symbolic links, so both islink() and isdir() +# can be true for the same path on systems that support symlinks +def isdir(s): + """Return true if the pathname refers to an existing directory.""" + try: + st = os.stat(s) + except os.error: + return False + return stat.S_ISDIR(st.st_mode) + + +def getsize(filename): + """Return the size of a file, reported by os.stat().""" + return os.stat(filename).st_size + + +def getmtime(filename): + """Return the last modification time of a file, reported by os.stat().""" + return os.stat(filename).st_mtime + + +def getatime(filename): + """Return the last access time of a file, reported by os.stat().""" + return os.stat(filename).st_atime + + +def getctime(filename): + """Return the metadata change time of a file, reported by os.stat().""" + return os.stat(filename).st_ctime + + +# Return the longest prefix of all list elements. +def commonprefix(m): + "Given a list of pathnames, returns the longest common leading component" + if not m: return '' + s1 = min(m) + s2 = max(m) + n = min(len(s1), len(s2)) + for i in xrange(n): + if s1[i] != s2[i]: + return s1[:i] + return s1[:n] + diff --git a/Lib/macpath.py b/Lib/macpath.py index f93ceb15401..d389d70a97c 100644 --- a/Lib/macpath.py +++ b/Lib/macpath.py @@ -2,6 +2,7 @@ import os from stat import * +from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -101,31 +102,6 @@ def ismount(s): components = split(s) return len(components) == 2 and components[1] == '' -def isdir(s): - """Return true if the pathname refers to an existing directory.""" - - try: - st = os.stat(s) - except os.error: - return 0 - return S_ISDIR(st.st_mode) - - -# Get size, mtime, atime of files. - -def getsize(filename): - """Return the size of a file, reported by os.stat().""" - return os.stat(filename).st_size - -def getmtime(filename): - """Return the last modification time of a file, reported by os.stat().""" - return os.stat(filename).st_mtime - -def getatime(filename): - """Return the last access time of a file, reported by os.stat().""" - return os.stat(filename).st_atime - - def islink(s): """Return true if the pathname refers to a symbolic link.""" @@ -135,29 +111,6 @@ def islink(s): except: return False - -def isfile(s): - """Return true if the pathname refers to an existing regular file.""" - - try: - st = os.stat(s) - except os.error: - return False - return S_ISREG(st.st_mode) - -def getctime(filename): - """Return the creation time of a file, reported by os.stat().""" - return os.stat(filename).st_ctime - -def exists(s): - """Test whether a path exists. Returns False for broken symbolic links""" - - try: - st = os.stat(s) - except os.error: - return False - return True - # Is `stat`/`lstat` a meaningful difference on the Mac? This is safe in any # case. @@ -170,20 +123,6 @@ def lexists(path): return False return True -# Return the longest prefix of all list elements. - -def commonprefix(m): - "Given a list of pathnames, returns the longest common leading component" - if not m: return '' - s1 = min(m) - s2 = max(m) - n = min(len(s1), len(s2)) - for i in xrange(n): - if s1[i] != s2[i]: - return s1[:i] - return s1[:n] - - def expandvars(path): """Dummy to retain interface-compatibility with other operating systems.""" return path diff --git a/Lib/ntpath.py b/Lib/ntpath.py index 7a79b5322f1..b32ec164bb9 100644 --- a/Lib/ntpath.py +++ b/Lib/ntpath.py @@ -8,6 +8,7 @@ module as os.path. import os import stat import sys +from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -206,86 +207,18 @@ def dirname(p): """Returns the directory component of a pathname""" return split(p)[0] - -# Return the longest prefix of all list elements. - -def commonprefix(m): - "Given a list of pathnames, returns the longest common leading component" - if not m: return '' - s1 = min(m) - s2 = max(m) - n = min(len(s1), len(s2)) - for i in xrange(n): - if s1[i] != s2[i]: - return s1[:i] - return s1[:n] - - -# Get size, mtime, atime of files. - -def getsize(filename): - """Return the size of a file, reported by os.stat()""" - return os.stat(filename).st_size - -def getmtime(filename): - """Return the last modification time of a file, reported by os.stat()""" - return os.stat(filename).st_mtime - -def getatime(filename): - """Return the last access time of a file, reported by os.stat()""" - return os.stat(filename).st_atime - -def getctime(filename): - """Return the creation time of a file, reported by os.stat().""" - return os.stat(filename).st_ctime - # Is a path a symbolic link? # This will always return false on systems where posix.lstat doesn't exist. def islink(path): - """Test for symbolic link. On WindowsNT/95 always returns false""" + """Test for symbolic link. + On WindowsNT/95 and OS/2 always returns false + """ return False - -# Does a path exist? - -def exists(path): - """Test whether a path exists""" - try: - st = os.stat(path) - except os.error: - return False - return True - +# alias exists to lexists lexists = exists - -# Is a path a dos directory? -# This follows symbolic links, so both islink() and isdir() can be true -# for the same path. - -def isdir(path): - """Test whether a path is a directory""" - try: - st = os.stat(path) - except os.error: - return False - return stat.S_ISDIR(st.st_mode) - - -# Is a path a regular file? -# This follows symbolic links, so both islink() and isdir() can be true -# for the same path. - -def isfile(path): - """Test whether a path is a regular file""" - try: - st = os.stat(path) - except os.error: - return False - return stat.S_ISREG(st.st_mode) - - # Is a path a mount point? Either a root (with or without drive letter) # or an UNC path with at most a / or \ after the mount point. diff --git a/Lib/os2emxpath.py b/Lib/os2emxpath.py index a8414224043..4e85c4d48df 100644 --- a/Lib/os2emxpath.py +++ b/Lib/os2emxpath.py @@ -7,6 +7,9 @@ module as os.path. import os import stat +from genericpath import * +from ntpath import (expanduser, expandvars, isabs, islink, splitdrive, + splitext, split, walk) __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -36,18 +39,6 @@ def normcase(s): return s.replace('\\', '/').lower() -# Return whether a path is absolute. -# Trivial in Posix, harder on the Mac or MS-DOS. -# For DOS it is absolute if it starts with a slash or backslash (current -# volume), or if a pathname after the volume letter and colon / UNC resource -# starts with a slash or backslash. - -def isabs(s): - """Test whether a path is absolute""" - s = splitdrive(s)[1] - return s != '' and s[:1] in '/\\' - - # Join two (or more) paths. def join(a, *p): @@ -63,17 +54,6 @@ def join(a, *p): return path -# Split a path in a drive specification (a drive letter followed by a -# colon) and the path specification. -# It is always true that drivespec + pathspec == p -def splitdrive(p): - """Split a pathname into drive and path specifiers. Returns a 2-tuple -"(drive,path)"; either part may be empty""" - if p[1:2] == ':': - return p[0:2], p[2:] - return '', p - - # Parse UNC paths def splitunc(p): """Split a pathname into UNC mount point and relative path specifiers. @@ -103,57 +83,6 @@ def splitunc(p): return '', p -# Split a path in head (everything up to the last '/') and tail (the -# rest). After the trailing '/' is stripped, the invariant -# join(head, tail) == p holds. -# The resulting head won't end in '/' unless it is the root. - -def split(p): - """Split a pathname. - - Return tuple (head, tail) where tail is everything after the final slash. - Either part may be empty.""" - - d, p = splitdrive(p) - # set i to index beyond p's last slash - i = len(p) - while i and p[i-1] not in '/\\': - i = i - 1 - head, tail = p[:i], p[i:] # now tail has no slashes - # remove trailing slashes from head, unless it's all slashes - head2 = head - while head2 and head2[-1] in '/\\': - head2 = head2[:-1] - head = head2 or head - return d + head, tail - - -# Split a path in root and extension. -# The extension is everything starting at the last dot in the last -# pathname component; the root is everything before that. -# It is always true that root + ext == p. - -def splitext(p): - """Split the extension from a pathname. - - Extension is everything from the last dot to the end. - Return (root, ext), either part may be empty.""" - root, ext = '', '' - for c in p: - if c in ['/','\\']: - root, ext = root + ext + c, '' - elif c == '.': - if ext: - root, ext = root + ext, c - else: - ext = c - elif ext: - ext = ext + c - else: - root = root + c - return root, ext - - # Return the tail (basename) part of a path. def basename(p): @@ -168,84 +97,12 @@ def dirname(p): return split(p)[0] -# Return the longest prefix of all list elements. - -def commonprefix(m): - "Given a list of pathnames, returns the longest common leading component" - if not m: return '' - s1 = min(m) - s2 = max(m) - n = min(len(s1), len(s2)) - for i in xrange(n): - if s1[i] != s2[i]: - return s1[:i] - return s1[:n] - - -# Get size, mtime, atime of files. - -def getsize(filename): - """Return the size of a file, reported by os.stat()""" - return os.stat(filename).st_size - -def getmtime(filename): - """Return the last modification time of a file, reported by os.stat()""" - return os.stat(filename).st_mtime - -def getatime(filename): - """Return the last access time of a file, reported by os.stat()""" - return os.stat(filename).st_atime - -def getctime(filename): - """Return the creation time of a file, reported by os.stat().""" - return os.stat(filename).st_ctime - -# Is a path a symbolic link? -# This will always return false on systems where posix.lstat doesn't exist. - -def islink(path): - """Test for symbolic link. On OS/2 always returns false""" - return False - - -# Does a path exist? -# This is false for dangling symbolic links. - -def exists(path): - """Test whether a path exists""" - try: - st = os.stat(path) - except os.error: - return False - return True - +# alias exists to lexists lexists = exists # Is a path a directory? -def isdir(path): - """Test whether a path is a directory""" - try: - st = os.stat(path) - except os.error: - return False - return stat.S_ISDIR(st.st_mode) - - -# Is a path a regular file? -# This follows symbolic links, so both islink() and isdir() can be true -# for the same path. - -def isfile(path): - """Test whether a path is a regular file""" - try: - st = os.stat(path) - except os.error: - return False - return stat.S_ISREG(st.st_mode) - - # Is a path a mount point? Either a root (with or without drive letter) # or an UNC path with at most a / or \ after the mount point. @@ -258,131 +115,6 @@ def ismount(path): return len(p) == 1 and p[0] in '/\\' -# Directory tree walk. -# For each directory under top (including top itself, but excluding -# '.' and '..'), func(arg, dirname, filenames) is called, where -# dirname is the name of the directory and filenames is the list -# of files (and subdirectories etc.) in the directory. -# The func may modify the filenames list, to implement a filter, -# or to impose a different order of visiting. - -def walk(top, func, arg): - """Directory tree walk whth callback function. - - walk(top, func, arg) calls func(arg, d, files) for each directory d - in the tree rooted at top (including top itself); files is a list - of all the files and subdirs in directory d.""" - try: - names = os.listdir(top) - except os.error: - return - func(arg, top, names) - exceptions = ('.', '..') - for name in names: - if name not in exceptions: - name = join(top, name) - if isdir(name): - walk(name, func, arg) - - -# Expand paths beginning with '~' or '~user'. -# '~' means $HOME; '~user' means that user's home directory. -# If the path doesn't begin with '~', or if the user or $HOME is unknown, -# the path is returned unchanged (leaving error reporting to whatever -# function is called with the expanded path as argument). -# See also module 'glob' for expansion of *, ? and [...] in pathnames. -# (A function should also be defined to do full *sh-style environment -# variable expansion.) - -def expanduser(path): - """Expand ~ and ~user constructs. - - If user or $HOME is unknown, do nothing.""" - if path[:1] != '~': - return path - i, n = 1, len(path) - while i < n and path[i] not in '/\\': - i = i + 1 - if i == 1: - if 'HOME' in os.environ: - userhome = os.environ['HOME'] - elif not 'HOMEPATH' in os.environ: - return path - else: - try: - drive = os.environ['HOMEDRIVE'] - except KeyError: - drive = '' - userhome = join(drive, os.environ['HOMEPATH']) - else: - return path - return userhome + path[i:] - - -# Expand paths containing shell variable substitutions. -# The following rules apply: -# - no expansion within single quotes -# - no escape character, except for '$$' which is translated into '$' -# - ${varname} is accepted. -# - varnames can be made out of letters, digits and the character '_' -# XXX With COMMAND.COM you can use any characters in a variable name, -# XXX except '^|<>='. - -def expandvars(path): - """Expand shell variables of form $var and ${var}. - - Unknown variables are left unchanged.""" - if '$' not in path: - return path - import string - varchars = string.letters + string.digits + '_-' - res = '' - index = 0 - pathlen = len(path) - while index < pathlen: - c = path[index] - if c == '\'': # no expansion within single quotes - path = path[index + 1:] - pathlen = len(path) - try: - index = path.index('\'') - res = res + '\'' + path[:index + 1] - except ValueError: - res = res + path - index = pathlen - 1 - elif c == '$': # variable or '$$' - if path[index + 1:index + 2] == '$': - res = res + c - index = index + 1 - elif path[index + 1:index + 2] == '{': - path = path[index+2:] - pathlen = len(path) - try: - index = path.index('}') - var = path[:index] - if var in os.environ: - res = res + os.environ[var] - except ValueError: - res = res + path - index = pathlen - 1 - else: - var = '' - index = index + 1 - c = path[index:index + 1] - while c != '' and c in varchars: - var = var + c - index = index + 1 - c = path[index:index + 1] - if var in os.environ: - res = res + os.environ[var] - if c != '': - res = res + c - else: - res = res + c - index = index + 1 - return res - - # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. def normpath(path): diff --git a/Lib/posixpath.py b/Lib/posixpath.py index 9eac6bc13eb..9dd0e9079d0 100644 --- a/Lib/posixpath.py +++ b/Lib/posixpath.py @@ -12,6 +12,7 @@ for manipulation of the pathname component of URLs. import os import stat +from genericpath import * __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -119,37 +120,6 @@ def dirname(p): return split(p)[0] -# Return the longest prefix of all list elements. - -def commonprefix(m): - "Given a list of pathnames, returns the longest common leading component" - if not m: return '' - s1 = min(m) - s2 = max(m) - n = min(len(s1), len(s2)) - for i in xrange(n): - if s1[i] != s2[i]: - return s1[:i] - return s1[:n] - -# Get size, mtime, atime of files. - -def getsize(filename): - """Return the size of a file, reported by os.stat().""" - return os.stat(filename).st_size - -def getmtime(filename): - """Return the last modification time of a file, reported by os.stat().""" - return os.stat(filename).st_mtime - -def getatime(filename): - """Return the last access time of a file, reported by os.stat().""" - return os.stat(filename).st_atime - -def getctime(filename): - """Return the metadata change time of a file, reported by os.stat().""" - return os.stat(filename).st_ctime - # Is a path a symbolic link? # This will always return false on systems where os.lstat doesn't exist. @@ -161,19 +131,6 @@ def islink(path): return False return stat.S_ISLNK(st.st_mode) - -# Does a path exist? -# This is false for dangling symbolic links. - -def exists(path): - """Test whether a path exists. Returns False for broken symbolic links""" - try: - st = os.stat(path) - except os.error: - return False - return True - - # Being true for dangling symbolic links is also useful. def lexists(path): @@ -185,32 +142,6 @@ def lexists(path): return True -# Is a path a directory? -# This follows symbolic links, so both islink() and isdir() can be true -# for the same path. - -def isdir(path): - """Test whether a path is a directory""" - try: - st = os.stat(path) - except os.error: - return False - return stat.S_ISDIR(st.st_mode) - - -# Is a path a regular file? -# This follows symbolic links, so both islink() and isfile() can be true -# for the same path. - -def isfile(path): - """Test whether a path is a regular file""" - try: - st = os.stat(path) - except os.error: - return False - return stat.S_ISREG(st.st_mode) - - # Are two filenames really pointing to the same file? def samefile(f1, f2): diff --git a/Lib/test/test_genericpath.py b/Lib/test/test_genericpath.py new file mode 100644 index 00000000000..53c4607ee2c --- /dev/null +++ b/Lib/test/test_genericpath.py @@ -0,0 +1,184 @@ +import unittest +from test import test_support +import os +import genericpath + +class AllCommonTest(unittest.TestCase): + + def assertIs(self, a, b): + self.assert_(a is b) + + def test_commonprefix(self): + self.assertEqual( + genericpath.commonprefix([]), + "" + ) + self.assertEqual( + genericpath.commonprefix(["/home/swenson/spam", "/home/swen/spam"]), + "/home/swen" + ) + self.assertEqual( + genericpath.commonprefix(["/home/swen/spam", "/home/swen/eggs"]), + "/home/swen/" + ) + self.assertEqual( + genericpath.commonprefix(["/home/swen/spam", "/home/swen/spam"]), + "/home/swen/spam" + ) + + def test_getsize(self): + f = open(test_support.TESTFN, "wb") + try: + f.write("foo") + f.close() + self.assertEqual(genericpath.getsize(test_support.TESTFN), 3) + finally: + if not f.closed: + f.close() + os.remove(test_support.TESTFN) + + def test_time(self): + f = open(test_support.TESTFN, "wb") + try: + f.write("foo") + f.close() + f = open(test_support.TESTFN, "ab") + f.write("bar") + f.close() + f = open(test_support.TESTFN, "rb") + d = f.read() + f.close() + self.assertEqual(d, "foobar") + + self.assert_( + genericpath.getctime(test_support.TESTFN) <= + genericpath.getmtime(test_support.TESTFN) + ) + finally: + if not f.closed: + f.close() + os.remove(test_support.TESTFN) + + def test_exists(self): + self.assertIs(genericpath.exists(test_support.TESTFN), False) + f = open(test_support.TESTFN, "wb") + try: + f.write("foo") + f.close() + self.assertIs(genericpath.exists(test_support.TESTFN), True) + finally: + if not f.close(): + f.close() + try: + os.remove(test_support.TESTFN) + except os.error: + pass + + self.assertRaises(TypeError, genericpath.exists) + + def test_isdir(self): + self.assertIs(genericpath.isdir(test_support.TESTFN), False) + f = open(test_support.TESTFN, "wb") + try: + f.write("foo") + f.close() + self.assertIs(genericpath.isdir(test_support.TESTFN), False) + os.remove(test_support.TESTFN) + os.mkdir(test_support.TESTFN) + self.assertIs(genericpath.isdir(test_support.TESTFN), True) + os.rmdir(test_support.TESTFN) + finally: + if not f.close(): + f.close() + try: + os.remove(test_support.TESTFN) + except os.error: + pass + try: + os.rmdir(test_support.TESTFN) + except os.error: + pass + + self.assertRaises(TypeError, genericpath.isdir) + + def test_isfile(self): + self.assertIs(genericpath.isfile(test_support.TESTFN), False) + f = open(test_support.TESTFN, "wb") + try: + f.write("foo") + f.close() + self.assertIs(genericpath.isfile(test_support.TESTFN), True) + os.remove(test_support.TESTFN) + os.mkdir(test_support.TESTFN) + self.assertIs(genericpath.isfile(test_support.TESTFN), False) + os.rmdir(test_support.TESTFN) + finally: + if not f.close(): + f.close() + try: + os.remove(test_support.TESTFN) + except os.error: + pass + try: + os.rmdir(test_support.TESTFN) + except os.error: + pass + + self.assertRaises(TypeError, genericpath.isdir) + + def test_samefile(self): + f = open(test_support.TESTFN + "1", "wb") + try: + f.write("foo") + f.close() + self.assertIs( + genericpath.samefile( + test_support.TESTFN + "1", + test_support.TESTFN + "1" + ), + True + ) + # If we don't have links, assume that os.stat doesn't return resonable + # inode information and thus, that samefile() doesn't work + if hasattr(os, "symlink"): + os.symlink( + test_support.TESTFN + "1", + test_support.TESTFN + "2" + ) + self.assertIs( + genericpath.samefile( + test_support.TESTFN + "1", + test_support.TESTFN + "2" + ), + True + ) + os.remove(test_support.TESTFN + "2") + f = open(test_support.TESTFN + "2", "wb") + f.write("bar") + f.close() + self.assertIs( + genericpath.samefile( + test_support.TESTFN + "1", + test_support.TESTFN + "2" + ), + False + ) + finally: + if not f.close(): + f.close() + try: + os.remove(test_support.TESTFN + "1") + except os.error: + pass + try: + os.remove(test_support.TESTFN + "2") + except os.error: + pass + + self.assertRaises(TypeError, genericpath.samefile) + +def test_main(): + test_support.run_unittest(AllCommonTest) + +if __name__=="__main__": + test_main()