source: python/trunk/Lib/os2knixpath.py@ 607

Last change on this file since 607 was 607, checked in by dmik, 10 years ago

python: Replace altsep to sep in paths returned by os.path.join.

This makes the returned paths consistent and fixes failures in situations
where the result is further passed to a unix shell (and similar cases).
See #148 for more details.

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1# Module 'os2emxpath' -- common operations on OS/2 pathnames
2"""Common pathname manipulations, OS/2 EMX version.
3
4Instead of importing this module directly, import os and refer to this
5module as os.path.
6"""
7
8import os
9import stat
10from genericpath import *
11from ntpath import (expanduser, expandvars, isabs, islink, splitdrive,
12 splitext, split, walk)
13
14__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
15 "basename","dirname","commonprefix","getsize","getmtime",
16 "getatime","getctime", "islink","exists","lexists","isdir","isfile",
17 "ismount","walk","expanduser","expandvars","normpath","abspath",
18 "samefile","sameopenfile","samestat",
19 "splitunc","curdir","pardir","sep","pathsep","defpath","altsep",
20 "extsep","devnull","realpath","supports_unicode_filenames"]
21
22# strings representing various path-related bits and pieces
23curdir = '.'
24pardir = '..'
25extsep = '.'
26sep = '/'
27altsep = '\\'
28pathsep = ';'
29defpath = '.;C:\\bin'
30devnull = 'nul'
31
32# Normalize the case of a pathname and map slashes to backslashes.
33# Other normalizations (such as optimizing '../' away) are not done
34# (this is done by normpath).
35
36def normcase(s):
37 """Normalize case of pathname.
38
39 Makes all characters lowercase and all altseps into seps."""
40 return s.replace(altsep, sep).lower()
41
42
43# Join two (or more) paths.
44
45def join(a, *p):
46 """Join two or more pathname components, inserting sep as needed.
47
48 Also replaces all altsep chars with sep in the returned string
49 to make it consistent."""
50 path = a
51 for b in p:
52 if isabs(b):
53 path = b
54 elif path == '' or path[-1:] in '/\\:':
55 path = path + b
56 else:
57 path = path + '/' + b
58 return path.replace(altsep, sep)
59
60
61# Parse UNC paths
62def splitunc(p):
63 """Split a pathname into UNC mount point and relative path specifiers.
64
65 Return a 2-tuple (unc, rest); either part may be empty.
66 If unc is not empty, it has the form '//host/mount' (or similar
67 using backslashes). unc+rest is always the input path.
68 Paths containing drive letters never have an UNC part.
69 """
70 if p[1:2] == ':':
71 return '', p # Drive letter present
72 firstTwo = p[0:2]
73 if firstTwo == '/' * 2 or firstTwo == '\\' * 2:
74 # is a UNC path:
75 # vvvvvvvvvvvvvvvvvvvv equivalent to drive letter
76 # \\machine\mountpoint\directories...
77 # directory ^^^^^^^^^^^^^^^
78 normp = normcase(p)
79 index = normp.find('/', 2)
80 if index == -1:
81 ##raise RuntimeError, 'illegal UNC path: "' + p + '"'
82 return ("", p)
83 index = normp.find('/', index + 1)
84 if index == -1:
85 index = len(p)
86 return p[:index], p[index:]
87 return '', p
88
89
90# Return the tail (basename) part of a path.
91
92def basename(p):
93 """Returns the final component of a pathname"""
94 return split(p)[1]
95
96
97# Return the head (dirname) part of a path.
98
99def dirname(p):
100 """Returns the directory component of a pathname"""
101 return split(p)[0]
102
103
104# Is a path a directory?
105
106# Is a path a mount point? Either a root (with or without drive letter)
107# or an UNC path with at most a / or \ after the mount point.
108
109def ismount(path):
110 """Test whether a path is a mount point (defined as root of drive)"""
111 unc, rest = splitunc(path)
112 if unc:
113 return rest in ("", "/", "\\")
114 p = splitdrive(path)[1]
115 return len(p) == 1 and p[0] in '/\\'
116
117
118# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.
119
120def normpath(path):
121 """Normalize path, eliminating double slashes, etc."""
122 path = path.replace('\\', '/')
123 prefix, path = splitdrive(path)
124 while path[:1] == '/':
125 prefix = prefix + '/'
126 path = path[1:]
127 comps = path.split('/')
128 i = 0
129 while i < len(comps):
130 if comps[i] == '.':
131 del comps[i]
132 elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
133 del comps[i-1:i+1]
134 i = i - 1
135 elif comps[i] == '' and i > 0 and comps[i-1] != '':
136 del comps[i]
137 else:
138 i = i + 1
139 # If the path is now empty, substitute '.'
140 if not prefix and not comps:
141 comps.append('.')
142 return prefix + '/'.join(comps)
143
144
145# Return an absolute path.
146def abspath(path):
147 """Return the absolute version of a path"""
148 if not isabs(path):
149 path = join(os.getcwd(), path)
150 return normpath(path)
151
152
153# Return a canonical path (i.e. the absolute location of a file on the
154# filesystem).
155
156def realpath(filename):
157 """Return the canonical path of the specified filename, eliminating any
158symbolic links encountered in the path."""
159 if isabs(filename):
160 bits = ['/'] + filename.split('/')[1:]
161 else:
162 bits = [''] + filename.split('/')
163
164 for i in range(2, len(bits)+1):
165 component = join(*bits[0:i])
166 # Resolve symbolic links.
167 if islink(component):
168 resolved = _resolve_link(component)
169 if resolved is None:
170 # Infinite loop -- return original component + rest of the path
171 return abspath(join(*([component] + bits[i:])))
172 else:
173 newpath = join(*([resolved] + bits[i:]))
174 return realpath(newpath)
175
176 return abspath(filename)
177
178
179def _resolve_link(path):
180 """Internal helper function. Takes a path and follows symlinks
181 until we either arrive at something that isn't a symlink, or
182 encounter a path we've seen before (meaning that there's a loop).
183 """
184 paths_seen = []
185 while islink(path):
186 if path in paths_seen:
187 # Already seen this path, so we must have a symlink loop
188 return None
189 paths_seen.append(path)
190 # Resolve where the link points to
191 resolved = os.readlink(path)
192 if not isabs(resolved):
193 dir = dirname(path)
194 path = normpath(join(dir, resolved))
195 else:
196 path = normpath(resolved)
197 return path
198
199
200# Is a path a symbolic link?
201# This will always return false on systems where os.lstat doesn't exist.
202
203def islink(path):
204 """Test whether a path is a symbolic link"""
205 try:
206 st = os.lstat(path)
207 except (os.error, AttributeError):
208 return False
209 return stat.S_ISLNK(st.st_mode)
210
211# Being true for dangling symbolic links is also useful.
212
213def lexists(path):
214 """Test whether a path exists. Returns True for broken symbolic links"""
215 try:
216 st = os.lstat(path)
217 except os.error:
218 return False
219 return True
220
221
222# Are two filenames really pointing to the same file?
223
224def samefile(f1, f2):
225 """Test whether two pathnames reference the same actual file"""
226 s1 = os.stat(f1)
227 s2 = os.stat(f2)
228 return samestat(s1, s2)
229
230
231# Are two open files really referencing the same file?
232# (Not necessarily the same file descriptor!)
233
234def sameopenfile(fp1, fp2):
235 """Test whether two open file objects reference the same file"""
236 s1 = os.fstat(fp1)
237 s2 = os.fstat(fp2)
238 return samestat(s1, s2)
239
240
241# Are two stat buffers (obtained from stat, fstat or lstat)
242# describing the same file?
243
244def samestat(s1, s2):
245 """Test whether two stat buffers reference the same file"""
246 return s1.st_ino == s2.st_ino and \
247 s1.st_dev == s2.st_dev
248
249
250supports_unicode_filenames = False
251
252
253def relpath(path, start=curdir):
254 """Return a relative version of a path"""
255
256 if not path:
257 raise ValueError("no path specified")
258 start_list = abspath(start).split(sep)
259 path_list = abspath(path).split(sep)
260 # Remove empty components after trailing slashes
261 if (start_list[-1] == ''):
262 start_list.pop()
263 if (path_list[-1] == ''):
264 path_list.pop()
265 if start_list[0].lower() != path_list[0].lower():
266 unc_path, rest = splitunc(path)
267 unc_start, rest = splitunc(start)
268 if bool(unc_path) ^ bool(unc_start):
269 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
270 % (path, start))
271 else:
272 raise ValueError("path is on drive %s, start on drive %s"
273 % (path_list[0], start_list[0]))
274 # Work out how much of the filepath is shared by start and path.
275 for i in range(min(len(start_list), len(path_list))):
276 if start_list[i].lower() != path_list[i].lower():
277 break
278 else:
279 i += 1
280
281 rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
282 if not rel_list:
283 return curdir
284 return join(*rel_list)
Note: See TracBrowser for help on using the repository browser.