blob: df333864a8a6fa1ce8e4f783a9e6a417a3077e4d [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001"""Common pathname manipulations, JDK version.
2
3Instead of importing this module directly, import os and refer to this
4module 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
16import stat
17import sys
18from java.io import File
19import java.io.IOException
20from java.lang import System
21import os
22
23from org.python.core.Py import newString as asPyString
24
25
26def _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
32def _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
42def 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
53def basename(path):
54 """Return the final component of a pathname"""
55 path = _tostr(path, "basename")
56 return asPyString(File(path).getName())
57
58def 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
68def 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
85def 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
96def 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
105def isabs(path):
106 """Test whether a path is absolute"""
107 path = _tostr(path, "isabs")
108 return File(path).isAbsolute()
109
110def isfile(path):
111 """Test whether a path is a regular file"""
112 path = _tostr(path, "isfile")
113 return File(sys.getPath(path)).isFile()
114
115def isdir(path):
116 """Test whether a path is a directory"""
117 path = _tostr(path, "isdir")
118 return File(sys.getPath(path)).isDirectory()
119
120def 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
135def 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
144def 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
156def 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
164def 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
170def 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
178def 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
196def 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
205def getuser():
206 return System.getProperty("user.name")
207
208def 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!
217def 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
249def abspath(path):
250 """Return an absolute path normalized but symbolic links not eliminated"""
251 path = _tostr(path, "abspath")
252 return _abspath(path)
253
254def _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
259def realpath(path):
260 """Return an absolute path normalized and symbolic links eliminated"""
261 path = _tostr(path, "realpath")
262 return _realpath(path)
263
264def _realpath(path):
265 try:
266 return asPyString(File(sys.getPath(path)).getCanonicalPath())
267 except java.io.IOException:
268 return _abspath(path)
269
270def 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
280def 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
287def 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
308def 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