| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame^] | 1 | """Common pathname manipulations, JDK version. |
| 2 | |
| 3 | Instead of importing this module directly, import os and refer to this |
| 4 | module as os.path. |
| 5 | |
| 6 | """ |
| 7 | |
| 8 | # Incompletely implemented: |
| 9 | # ismount -- How? |
| 10 | # normcase -- How? |
| 11 | |
| 12 | # Missing: |
| 13 | # sameopenfile -- Java doesn't have fstat nor file descriptors? |
| 14 | # samestat -- How? |
| 15 | |
| 16 | import stat |
| 17 | import sys |
| 18 | from java.io import File |
| 19 | import java.io.IOException |
| 20 | from java.lang import System |
| 21 | import os |
| 22 | |
| 23 | from org.python.core.Py import newString as asPyString |
| 24 | |
| 25 | |
| 26 | def _tostr(s, method): |
| 27 | if isinstance(s, basestring): |
| 28 | return s |
| 29 | raise TypeError, "%s() argument must be a str or unicode object, not %s" % ( |
| 30 | method, _type_name(s)) |
| 31 | |
| 32 | def _type_name(obj): |
| 33 | TPFLAGS_HEAPTYPE = 1 << 9 |
| 34 | type_name = '' |
| 35 | obj_type = type(obj) |
| 36 | is_heap = obj_type.__flags__ & TPFLAGS_HEAPTYPE == TPFLAGS_HEAPTYPE |
| 37 | if not is_heap and obj_type.__module__ != '__builtin__': |
| 38 | type_name = '%s.' % obj_type.__module__ |
| 39 | type_name += obj_type.__name__ |
| 40 | return type_name |
| 41 | |
| 42 | def dirname(path): |
| 43 | """Return the directory component of a pathname""" |
| 44 | path = _tostr(path, "dirname") |
| 45 | result = asPyString(File(path).getParent()) |
| 46 | if not result: |
| 47 | if isabs(path): |
| 48 | result = path # Must be root |
| 49 | else: |
| 50 | result = "" |
| 51 | return result |
| 52 | |
| 53 | def basename(path): |
| 54 | """Return the final component of a pathname""" |
| 55 | path = _tostr(path, "basename") |
| 56 | return asPyString(File(path).getName()) |
| 57 | |
| 58 | def split(path): |
| 59 | """Split a pathname. |
| 60 | |
| 61 | Return tuple "(head, tail)" where "tail" is everything after the |
| 62 | final slash. Either part may be empty. |
| 63 | |
| 64 | """ |
| 65 | path = _tostr(path, "split") |
| 66 | return (dirname(path), basename(path)) |
| 67 | |
| 68 | def splitext(path): |
| 69 | """Split the extension from a pathname. |
| 70 | |
| 71 | Extension is everything from the last dot to the end. Return |
| 72 | "(root, ext)", either part may be empty. |
| 73 | |
| 74 | """ |
| 75 | i = 0 |
| 76 | n = -1 |
| 77 | for c in path: |
| 78 | if c == '.': n = i |
| 79 | i = i+1 |
| 80 | if n < 0: |
| 81 | return (path, "") |
| 82 | else: |
| 83 | return (path[:n], path[n:]) |
| 84 | |
| 85 | def splitdrive(path): |
| 86 | """Split a pathname into drive and path specifiers. |
| 87 | |
| 88 | Returns a 2-tuple "(drive,path)"; either part may be empty. |
| 89 | """ |
| 90 | # Algorithm based on CPython's ntpath.splitdrive and ntpath.isabs. |
| 91 | if path[1:2] == ':' and path[0].lower() in 'abcdefghijklmnopqrstuvwxyz' \ |
| 92 | and (path[2:] == '' or path[2] in '/\\'): |
| 93 | return path[:2], path[2:] |
| 94 | return '', path |
| 95 | |
| 96 | def exists(path): |
| 97 | """Test whether a path exists. |
| 98 | |
| 99 | Returns false for broken symbolic links. |
| 100 | |
| 101 | """ |
| 102 | path = _tostr(path, "exists") |
| 103 | return File(sys.getPath(path)).exists() |
| 104 | |
| 105 | def isabs(path): |
| 106 | """Test whether a path is absolute""" |
| 107 | path = _tostr(path, "isabs") |
| 108 | return File(path).isAbsolute() |
| 109 | |
| 110 | def isfile(path): |
| 111 | """Test whether a path is a regular file""" |
| 112 | path = _tostr(path, "isfile") |
| 113 | return File(sys.getPath(path)).isFile() |
| 114 | |
| 115 | def isdir(path): |
| 116 | """Test whether a path is a directory""" |
| 117 | path = _tostr(path, "isdir") |
| 118 | return File(sys.getPath(path)).isDirectory() |
| 119 | |
| 120 | def join(path, *args): |
| 121 | """Join two or more pathname components, inserting os.sep as needed""" |
| 122 | path = _tostr(path, "join") |
| 123 | f = File(path) |
| 124 | for a in args: |
| 125 | a = _tostr(a, "join") |
| 126 | g = File(a) |
| 127 | if g.isAbsolute() or len(f.getPath()) == 0: |
| 128 | f = g |
| 129 | else: |
| 130 | if a == "": |
| 131 | a = os.sep |
| 132 | f = File(f, a) |
| 133 | return asPyString(f.getPath()) |
| 134 | |
| 135 | def normcase(path): |
| 136 | """Normalize case of pathname. |
| 137 | |
| 138 | XXX Not done right under JDK. |
| 139 | |
| 140 | """ |
| 141 | path = _tostr(path, "normcase") |
| 142 | return asPyString(File(path).getPath()) |
| 143 | |
| 144 | def commonprefix(m): |
| 145 | "Given a list of pathnames, return the longest common leading component" |
| 146 | if not m: return '' |
| 147 | prefix = m[0] |
| 148 | for item in m: |
| 149 | for i in range(len(prefix)): |
| 150 | if prefix[:i+1] <> item[:i+1]: |
| 151 | prefix = prefix[:i] |
| 152 | if i == 0: return '' |
| 153 | break |
| 154 | return prefix |
| 155 | |
| 156 | def islink(path): |
| 157 | """Test whether a path is a symbolic link""" |
| 158 | try: |
| 159 | st = os.lstat(path) |
| 160 | except (os.error, AttributeError): |
| 161 | return False |
| 162 | return stat.S_ISLNK(st.st_mode) |
| 163 | |
| 164 | def samefile(path, path2): |
| 165 | """Test whether two pathnames reference the same actual file""" |
| 166 | path = _tostr(path, "samefile") |
| 167 | path2 = _tostr(path2, "samefile") |
| 168 | return _realpath(path) == _realpath(path2) |
| 169 | |
| 170 | def ismount(path): |
| 171 | """Test whether a path is a mount point. |
| 172 | |
| 173 | XXX This incorrectly always returns false under JDK. |
| 174 | |
| 175 | """ |
| 176 | return 0 |
| 177 | |
| 178 | def walk(top, func, arg): |
| 179 | """Walk a directory tree. |
| 180 | |
| 181 | walk(top,func,args) calls func(arg, d, files) for each directory |
| 182 | "d" in the tree rooted at "top" (including "top" itself). "files" |
| 183 | is a list of all the files and subdirs in directory "d". |
| 184 | |
| 185 | """ |
| 186 | try: |
| 187 | names = os.listdir(top) |
| 188 | except os.error: |
| 189 | return |
| 190 | func(arg, top, names) |
| 191 | for name in names: |
| 192 | name = join(top, name) |
| 193 | if isdir(name) and not islink(name): |
| 194 | walk(name, func, arg) |
| 195 | |
| 196 | def expanduser(path): |
| 197 | if path[:1] == "~": |
| 198 | c = path[1:2] |
| 199 | if not c: |
| 200 | return gethome() |
| 201 | if c == os.sep: |
| 202 | return asPyString(File(gethome(), path[2:]).getPath()) |
| 203 | return path |
| 204 | |
| 205 | def getuser(): |
| 206 | return System.getProperty("user.name") |
| 207 | |
| 208 | def gethome(): |
| 209 | return System.getProperty("user.home") |
| 210 | |
| 211 | |
| 212 | # normpath() from Python 1.5.2, with Java appropriate generalizations |
| 213 | |
| 214 | # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. |
| 215 | # It should be understood that this may change the meaning of the path |
| 216 | # if it contains symbolic links! |
| 217 | def normpath(path): |
| 218 | """Normalize path, eliminating double slashes, etc.""" |
| 219 | sep = os.sep |
| 220 | if sep == '\\': |
| 221 | path = path.replace("/", sep) |
| 222 | curdir = os.curdir |
| 223 | pardir = os.pardir |
| 224 | import string |
| 225 | # Treat initial slashes specially |
| 226 | slashes = '' |
| 227 | while path[:1] == sep: |
| 228 | slashes = slashes + sep |
| 229 | path = path[1:] |
| 230 | comps = string.splitfields(path, sep) |
| 231 | i = 0 |
| 232 | while i < len(comps): |
| 233 | if comps[i] == curdir: |
| 234 | del comps[i] |
| 235 | while i < len(comps) and comps[i] == '': |
| 236 | del comps[i] |
| 237 | elif comps[i] == pardir and i > 0 and comps[i-1] not in ('', pardir): |
| 238 | del comps[i-1:i+1] |
| 239 | i = i-1 |
| 240 | elif comps[i] == '' and i > 0 and comps[i-1] <> '': |
| 241 | del comps[i] |
| 242 | else: |
| 243 | i = i+1 |
| 244 | # If the path is now empty, substitute '.' |
| 245 | if not comps and not slashes: |
| 246 | comps.append(curdir) |
| 247 | return slashes + string.joinfields(comps, sep) |
| 248 | |
| 249 | def abspath(path): |
| 250 | """Return an absolute path normalized but symbolic links not eliminated""" |
| 251 | path = _tostr(path, "abspath") |
| 252 | return _abspath(path) |
| 253 | |
| 254 | def _abspath(path): |
| 255 | # Must use normpath separately because getAbsolutePath doesn't normalize |
| 256 | # and getCanonicalPath would eliminate symlinks. |
| 257 | return normpath(asPyString(File(sys.getPath(path)).getAbsolutePath())) |
| 258 | |
| 259 | def realpath(path): |
| 260 | """Return an absolute path normalized and symbolic links eliminated""" |
| 261 | path = _tostr(path, "realpath") |
| 262 | return _realpath(path) |
| 263 | |
| 264 | def _realpath(path): |
| 265 | try: |
| 266 | return asPyString(File(sys.getPath(path)).getCanonicalPath()) |
| 267 | except java.io.IOException: |
| 268 | return _abspath(path) |
| 269 | |
| 270 | def getsize(path): |
| 271 | path = _tostr(path, "getsize") |
| 272 | f = File(sys.getPath(path)) |
| 273 | size = f.length() |
| 274 | # Sadly, if the returned length is zero, we don't really know if the file |
| 275 | # is zero sized or does not exist. |
| 276 | if size == 0 and not f.exists(): |
| 277 | raise OSError(0, 'No such file or directory', path) |
| 278 | return size |
| 279 | |
| 280 | def getmtime(path): |
| 281 | path = _tostr(path, "getmtime") |
| 282 | f = File(sys.getPath(path)) |
| 283 | if not f.exists(): |
| 284 | raise OSError(0, 'No such file or directory', path) |
| 285 | return f.lastModified() / 1000.0 |
| 286 | |
| 287 | def getatime(path): |
| 288 | # We can't detect access time so we return modification time. This |
| 289 | # matches the behaviour in os.stat(). |
| 290 | path = _tostr(path, "getatime") |
| 291 | f = File(sys.getPath(path)) |
| 292 | if not f.exists(): |
| 293 | raise OSError(0, 'No such file or directory', path) |
| 294 | return f.lastModified() / 1000.0 |
| 295 | |
| 296 | |
| 297 | # expandvars is stolen from CPython-2.1.1's Lib/ntpath.py: |
| 298 | |
| 299 | # Expand paths containing shell variable substitutions. |
| 300 | # The following rules apply: |
| 301 | # - no expansion within single quotes |
| 302 | # - no escape character, except for '$$' which is translated into '$' |
| 303 | # - ${varname} is accepted. |
| 304 | # - varnames can be made out of letters, digits and the character '_' |
| 305 | # XXX With COMMAND.COM you can use any characters in a variable name, |
| 306 | # XXX except '^|<>='. |
| 307 | |
| 308 | def expandvars(path): |
| 309 | """Expand shell variables of form $var and ${var}. |
| 310 | |
| 311 | Unknown variables are left unchanged.""" |
| 312 | if '$' not in path: |
| 313 | return path |
| 314 | import string |
| 315 | varchars = string.letters + string.digits + '_-' |
| 316 | res = '' |
| 317 | index = 0 |
| 318 | pathlen = len(path) |
| 319 | while index < pathlen: |
| 320 | c = path[index] |
| 321 | if c == '\'': # no expansion within single quotes |
| 322 | path = path[index + 1:] |
| 323 | pathlen = len(path) |
| 324 | try: |
| 325 | index = path.index('\'') |
| 326 | res = res + '\'' + path[:index + 1] |
| 327 | except ValueError: |
| 328 | res = res + path |
| 329 | index = pathlen - 1 |
| 330 | elif c == '$': # variable or '$$' |
| 331 | if path[index + 1:index + 2] == '$': |
| 332 | res = res + c |
| 333 | index = index + 1 |
| 334 | elif path[index + 1:index + 2] == '{': |
| 335 | path = path[index+2:] |
| 336 | pathlen = len(path) |
| 337 | try: |
| 338 | index = path.index('}') |
| 339 | var = path[:index] |
| 340 | if os.environ.has_key(var): |
| 341 | res = res + os.environ[var] |
| 342 | except ValueError: |
| 343 | res = res + path |
| 344 | index = pathlen - 1 |
| 345 | else: |
| 346 | var = '' |
| 347 | index = index + 1 |
| 348 | c = path[index:index + 1] |
| 349 | while c != '' and c in varchars: |
| 350 | var = var + c |
| 351 | index = index + 1 |
| 352 | c = path[index:index + 1] |
| 353 | if os.environ.has_key(var): |
| 354 | res = res + os.environ[var] |
| 355 | if c != '': |
| 356 | res = res + c |
| 357 | else: |
| 358 | res = res + c |
| 359 | index = index + 1 |
| 360 | return res |