]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Common operations on Posix pathnames.\r |
2 | \r | |
3 | Instead of importing this module directly, import os and refer to\r | |
4 | this module as os.path. The "os.path" name is an alias for this\r | |
5 | module on Posix systems; on other systems (e.g. Mac, Windows),\r | |
6 | os.path provides the same operations in a manner specific to that\r | |
7 | platform, and is an alias to another module (e.g. macpath, ntpath).\r | |
8 | \r | |
9 | Some of this can actually be useful on non-Posix systems too, e.g.\r | |
10 | for manipulation of the pathname component of URLs.\r | |
11 | """\r | |
12 | \r | |
13 | import os\r | |
14 | import sys\r | |
15 | import stat\r | |
16 | import genericpath\r | |
17 | import warnings\r | |
18 | from genericpath import *\r | |
19 | \r | |
20 | __all__ = ["normcase","isabs","join","splitdrive","split","splitext",\r | |
21 | "basename","dirname","commonprefix","getsize","getmtime",\r | |
22 | "getatime","getctime","islink","exists","lexists","isdir","isfile",\r | |
23 | "ismount","walk","expanduser","expandvars","normpath","abspath",\r | |
24 | "samefile","sameopenfile","samestat",\r | |
25 | "curdir","pardir","sep","pathsep","defpath","altsep","extsep",\r | |
26 | "devnull","realpath","supports_unicode_filenames","relpath"]\r | |
27 | \r | |
28 | # strings representing various path-related bits and pieces\r | |
29 | curdir = '.'\r | |
30 | pardir = '..'\r | |
31 | extsep = '.'\r | |
32 | sep = '/'\r | |
33 | pathsep = ':'\r | |
34 | defpath = ':/bin:/usr/bin'\r | |
35 | altsep = None\r | |
36 | devnull = '/dev/null'\r | |
37 | \r | |
38 | # Normalize the case of a pathname. Trivial in Posix, string.lower on Mac.\r | |
39 | # On MS-DOS this may also turn slashes into backslashes; however, other\r | |
40 | # normalizations (such as optimizing '../' away) are not allowed\r | |
41 | # (another function should be defined to do that).\r | |
42 | \r | |
43 | def normcase(s):\r | |
44 | """Normalize case of pathname. Has no effect under Posix"""\r | |
45 | return s\r | |
46 | \r | |
47 | \r | |
48 | # Return whether a path is absolute.\r | |
49 | # Trivial in Posix, harder on the Mac or MS-DOS.\r | |
50 | \r | |
51 | def isabs(s):\r | |
52 | """Test whether a path is absolute"""\r | |
53 | return s.startswith('/')\r | |
54 | \r | |
55 | \r | |
56 | # Join pathnames.\r | |
57 | # Ignore the previous parts if a part is absolute.\r | |
58 | # Insert a '/' unless the first part is empty or already ends in '/'.\r | |
59 | \r | |
60 | def join(a, *p):\r | |
61 | """Join two or more pathname components, inserting '/' as needed.\r | |
62 | If any component is an absolute path, all previous path components\r | |
63 | will be discarded."""\r | |
64 | path = a\r | |
65 | for b in p:\r | |
66 | if b.startswith('/'):\r | |
67 | path = b\r | |
68 | elif path == '' or path.endswith('/'):\r | |
69 | path += b\r | |
70 | else:\r | |
71 | path += '/' + b\r | |
72 | return path\r | |
73 | \r | |
74 | \r | |
75 | # Split a path in head (everything up to the last '/') and tail (the\r | |
76 | # rest). If the path ends in '/', tail will be empty. If there is no\r | |
77 | # '/' in the path, head will be empty.\r | |
78 | # Trailing '/'es are stripped from head unless it is the root.\r | |
79 | \r | |
80 | def split(p):\r | |
81 | """Split a pathname. Returns tuple "(head, tail)" where "tail" is\r | |
82 | everything after the final slash. Either part may be empty."""\r | |
83 | i = p.rfind('/') + 1\r | |
84 | head, tail = p[:i], p[i:]\r | |
85 | if head and head != '/'*len(head):\r | |
86 | head = head.rstrip('/')\r | |
87 | return head, tail\r | |
88 | \r | |
89 | \r | |
90 | # Split a path in root and extension.\r | |
91 | # The extension is everything starting at the last dot in the last\r | |
92 | # pathname component; the root is everything before that.\r | |
93 | # It is always true that root + ext == p.\r | |
94 | \r | |
95 | def splitext(p):\r | |
96 | return genericpath._splitext(p, sep, altsep, extsep)\r | |
97 | splitext.__doc__ = genericpath._splitext.__doc__\r | |
98 | \r | |
99 | # Split a pathname into a drive specification and the rest of the\r | |
100 | # path. Useful on DOS/Windows/NT; on Unix, the drive is always empty.\r | |
101 | \r | |
102 | def splitdrive(p):\r | |
103 | """Split a pathname into drive and path. On Posix, drive is always\r | |
104 | empty."""\r | |
105 | return '', p\r | |
106 | \r | |
107 | \r | |
108 | # Return the tail (basename) part of a path, same as split(path)[1].\r | |
109 | \r | |
110 | def basename(p):\r | |
111 | """Returns the final component of a pathname"""\r | |
112 | i = p.rfind('/') + 1\r | |
113 | return p[i:]\r | |
114 | \r | |
115 | \r | |
116 | # Return the head (dirname) part of a path, same as split(path)[0].\r | |
117 | \r | |
118 | def dirname(p):\r | |
119 | """Returns the directory component of a pathname"""\r | |
120 | i = p.rfind('/') + 1\r | |
121 | head = p[:i]\r | |
122 | if head and head != '/'*len(head):\r | |
123 | head = head.rstrip('/')\r | |
124 | return head\r | |
125 | \r | |
126 | \r | |
127 | # Is a path a symbolic link?\r | |
128 | # This will always return false on systems where os.lstat doesn't exist.\r | |
129 | \r | |
130 | def islink(path):\r | |
131 | """Test whether a path is a symbolic link"""\r | |
132 | try:\r | |
133 | st = os.lstat(path)\r | |
134 | except (os.error, AttributeError):\r | |
135 | return False\r | |
136 | return stat.S_ISLNK(st.st_mode)\r | |
137 | \r | |
138 | # Being true for dangling symbolic links is also useful.\r | |
139 | \r | |
140 | def lexists(path):\r | |
141 | """Test whether a path exists. Returns True for broken symbolic links"""\r | |
142 | try:\r | |
143 | os.lstat(path)\r | |
144 | except os.error:\r | |
145 | return False\r | |
146 | return True\r | |
147 | \r | |
148 | \r | |
149 | # Are two filenames really pointing to the same file?\r | |
150 | \r | |
151 | def samefile(f1, f2):\r | |
152 | """Test whether two pathnames reference the same actual file"""\r | |
153 | s1 = os.stat(f1)\r | |
154 | s2 = os.stat(f2)\r | |
155 | return samestat(s1, s2)\r | |
156 | \r | |
157 | \r | |
158 | # Are two open files really referencing the same file?\r | |
159 | # (Not necessarily the same file descriptor!)\r | |
160 | \r | |
161 | def sameopenfile(fp1, fp2):\r | |
162 | """Test whether two open file objects reference the same file"""\r | |
163 | s1 = os.fstat(fp1)\r | |
164 | s2 = os.fstat(fp2)\r | |
165 | return samestat(s1, s2)\r | |
166 | \r | |
167 | \r | |
168 | # Are two stat buffers (obtained from stat, fstat or lstat)\r | |
169 | # describing the same file?\r | |
170 | \r | |
171 | def samestat(s1, s2):\r | |
172 | """Test whether two stat buffers reference the same file"""\r | |
173 | return s1.st_ino == s2.st_ino and \\r | |
174 | s1.st_dev == s2.st_dev\r | |
175 | \r | |
176 | \r | |
177 | # Is a path a mount point?\r | |
178 | # (Does this work for all UNIXes? Is it even guaranteed to work by Posix?)\r | |
179 | \r | |
180 | def ismount(path):\r | |
181 | """Test whether a path is a mount point"""\r | |
182 | if islink(path):\r | |
183 | # A symlink can never be a mount point\r | |
184 | return False\r | |
185 | try:\r | |
186 | s1 = os.lstat(path)\r | |
187 | s2 = os.lstat(join(path, '..'))\r | |
188 | except os.error:\r | |
189 | return False # It doesn't exist -- so not a mount point :-)\r | |
190 | dev1 = s1.st_dev\r | |
191 | dev2 = s2.st_dev\r | |
192 | if dev1 != dev2:\r | |
193 | return True # path/.. on a different device as path\r | |
194 | ino1 = s1.st_ino\r | |
195 | ino2 = s2.st_ino\r | |
196 | if ino1 == ino2:\r | |
197 | return True # path/.. is the same i-node as path\r | |
198 | return False\r | |
199 | \r | |
200 | \r | |
201 | # Directory tree walk.\r | |
202 | # For each directory under top (including top itself, but excluding\r | |
203 | # '.' and '..'), func(arg, dirname, filenames) is called, where\r | |
204 | # dirname is the name of the directory and filenames is the list\r | |
205 | # of files (and subdirectories etc.) in the directory.\r | |
206 | # The func may modify the filenames list, to implement a filter,\r | |
207 | # or to impose a different order of visiting.\r | |
208 | \r | |
209 | def walk(top, func, arg):\r | |
210 | """Directory tree walk with callback function.\r | |
211 | \r | |
212 | For each directory in the directory tree rooted at top (including top\r | |
213 | itself, but excluding '.' and '..'), call func(arg, dirname, fnames).\r | |
214 | dirname is the name of the directory, and fnames a list of the names of\r | |
215 | the files and subdirectories in dirname (excluding '.' and '..'). func\r | |
216 | may modify the fnames list in-place (e.g. via del or slice assignment),\r | |
217 | and walk will only recurse into the subdirectories whose names remain in\r | |
218 | fnames; this can be used to implement a filter, or to impose a specific\r | |
219 | order of visiting. No semantics are defined for, or required of, arg,\r | |
220 | beyond that arg is always passed to func. It can be used, e.g., to pass\r | |
221 | a filename pattern, or a mutable object designed to accumulate\r | |
222 | statistics. Passing None for arg is common."""\r | |
223 | warnings.warnpy3k("In 3.x, os.path.walk is removed in favor of os.walk.",\r | |
224 | stacklevel=2)\r | |
225 | try:\r | |
226 | names = os.listdir(top)\r | |
227 | except os.error:\r | |
228 | return\r | |
229 | func(arg, top, names)\r | |
230 | for name in names:\r | |
231 | name = join(top, name)\r | |
232 | try:\r | |
233 | st = os.lstat(name)\r | |
234 | except os.error:\r | |
235 | continue\r | |
236 | if stat.S_ISDIR(st.st_mode):\r | |
237 | walk(name, func, arg)\r | |
238 | \r | |
239 | \r | |
240 | # Expand paths beginning with '~' or '~user'.\r | |
241 | # '~' means $HOME; '~user' means that user's home directory.\r | |
242 | # If the path doesn't begin with '~', or if the user or $HOME is unknown,\r | |
243 | # the path is returned unchanged (leaving error reporting to whatever\r | |
244 | # function is called with the expanded path as argument).\r | |
245 | # See also module 'glob' for expansion of *, ? and [...] in pathnames.\r | |
246 | # (A function should also be defined to do full *sh-style environment\r | |
247 | # variable expansion.)\r | |
248 | \r | |
249 | def expanduser(path):\r | |
250 | """Expand ~ and ~user constructions. If user or $HOME is unknown,\r | |
251 | do nothing."""\r | |
252 | if not path.startswith('~'):\r | |
253 | return path\r | |
254 | i = path.find('/', 1)\r | |
255 | if i < 0:\r | |
256 | i = len(path)\r | |
257 | if i == 1:\r | |
258 | if 'HOME' not in os.environ:\r | |
259 | import pwd\r | |
260 | userhome = pwd.getpwuid(os.getuid()).pw_dir\r | |
261 | else:\r | |
262 | userhome = os.environ['HOME']\r | |
263 | else:\r | |
264 | import pwd\r | |
265 | try:\r | |
266 | pwent = pwd.getpwnam(path[1:i])\r | |
267 | except KeyError:\r | |
268 | return path\r | |
269 | userhome = pwent.pw_dir\r | |
270 | userhome = userhome.rstrip('/') or userhome\r | |
271 | return userhome + path[i:]\r | |
272 | \r | |
273 | \r | |
274 | # Expand paths containing shell variable substitutions.\r | |
275 | # This expands the forms $variable and ${variable} only.\r | |
276 | # Non-existent variables are left unchanged.\r | |
277 | \r | |
278 | _varprog = None\r | |
279 | \r | |
280 | def expandvars(path):\r | |
281 | """Expand shell variables of form $var and ${var}. Unknown variables\r | |
282 | are left unchanged."""\r | |
283 | global _varprog\r | |
284 | if '$' not in path:\r | |
285 | return path\r | |
286 | if not _varprog:\r | |
287 | import re\r | |
288 | _varprog = re.compile(r'\$(\w+|\{[^}]*\})')\r | |
289 | i = 0\r | |
290 | while True:\r | |
291 | m = _varprog.search(path, i)\r | |
292 | if not m:\r | |
293 | break\r | |
294 | i, j = m.span(0)\r | |
295 | name = m.group(1)\r | |
296 | if name.startswith('{') and name.endswith('}'):\r | |
297 | name = name[1:-1]\r | |
298 | if name in os.environ:\r | |
299 | tail = path[j:]\r | |
300 | path = path[:i] + os.environ[name]\r | |
301 | i = len(path)\r | |
302 | path += tail\r | |
303 | else:\r | |
304 | i = j\r | |
305 | return path\r | |
306 | \r | |
307 | \r | |
308 | # Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B.\r | |
309 | # It should be understood that this may change the meaning of the path\r | |
310 | # if it contains symbolic links!\r | |
311 | \r | |
312 | def normpath(path):\r | |
313 | """Normalize path, eliminating double slashes, etc."""\r | |
314 | # Preserve unicode (if path is unicode)\r | |
315 | slash, dot = (u'/', u'.') if isinstance(path, unicode) else ('/', '.')\r | |
316 | if path == '':\r | |
317 | return dot\r | |
318 | initial_slashes = path.startswith('/')\r | |
319 | # POSIX allows one or two initial slashes, but treats three or more\r | |
320 | # as single slash.\r | |
321 | if (initial_slashes and\r | |
322 | path.startswith('//') and not path.startswith('///')):\r | |
323 | initial_slashes = 2\r | |
324 | comps = path.split('/')\r | |
325 | new_comps = []\r | |
326 | for comp in comps:\r | |
327 | if comp in ('', '.'):\r | |
328 | continue\r | |
329 | if (comp != '..' or (not initial_slashes and not new_comps) or\r | |
330 | (new_comps and new_comps[-1] == '..')):\r | |
331 | new_comps.append(comp)\r | |
332 | elif new_comps:\r | |
333 | new_comps.pop()\r | |
334 | comps = new_comps\r | |
335 | path = slash.join(comps)\r | |
336 | if initial_slashes:\r | |
337 | path = slash*initial_slashes + path\r | |
338 | return path or dot\r | |
339 | \r | |
340 | \r | |
341 | def abspath(path):\r | |
342 | """Return an absolute path."""\r | |
343 | if not isabs(path):\r | |
344 | if isinstance(path, unicode):\r | |
345 | cwd = os.getcwdu()\r | |
346 | else:\r | |
347 | cwd = os.getcwd()\r | |
348 | path = join(cwd, path)\r | |
349 | return normpath(path)\r | |
350 | \r | |
351 | \r | |
352 | # Return a canonical path (i.e. the absolute location of a file on the\r | |
353 | # filesystem).\r | |
354 | \r | |
355 | def realpath(filename):\r | |
356 | """Return the canonical path of the specified filename, eliminating any\r | |
357 | symbolic links encountered in the path."""\r | |
358 | if isabs(filename):\r | |
359 | bits = ['/'] + filename.split('/')[1:]\r | |
360 | else:\r | |
361 | bits = [''] + filename.split('/')\r | |
362 | \r | |
363 | for i in range(2, len(bits)+1):\r | |
364 | component = join(*bits[0:i])\r | |
365 | # Resolve symbolic links.\r | |
366 | if islink(component):\r | |
367 | resolved = _resolve_link(component)\r | |
368 | if resolved is None:\r | |
369 | # Infinite loop -- return original component + rest of the path\r | |
370 | return abspath(join(*([component] + bits[i:])))\r | |
371 | else:\r | |
372 | newpath = join(*([resolved] + bits[i:]))\r | |
373 | return realpath(newpath)\r | |
374 | \r | |
375 | return abspath(filename)\r | |
376 | \r | |
377 | \r | |
378 | def _resolve_link(path):\r | |
379 | """Internal helper function. Takes a path and follows symlinks\r | |
380 | until we either arrive at something that isn't a symlink, or\r | |
381 | encounter a path we've seen before (meaning that there's a loop).\r | |
382 | """\r | |
383 | paths_seen = set()\r | |
384 | while islink(path):\r | |
385 | if path in paths_seen:\r | |
386 | # Already seen this path, so we must have a symlink loop\r | |
387 | return None\r | |
388 | paths_seen.add(path)\r | |
389 | # Resolve where the link points to\r | |
390 | resolved = os.readlink(path)\r | |
391 | if not isabs(resolved):\r | |
392 | dir = dirname(path)\r | |
393 | path = normpath(join(dir, resolved))\r | |
394 | else:\r | |
395 | path = normpath(resolved)\r | |
396 | return path\r | |
397 | \r | |
398 | supports_unicode_filenames = (sys.platform == 'darwin')\r | |
399 | \r | |
400 | def relpath(path, start=curdir):\r | |
401 | """Return a relative version of a path"""\r | |
402 | \r | |
403 | if not path:\r | |
404 | raise ValueError("no path specified")\r | |
405 | \r | |
406 | start_list = [x for x in abspath(start).split(sep) if x]\r | |
407 | path_list = [x for x in abspath(path).split(sep) if x]\r | |
408 | \r | |
409 | # Work out how much of the filepath is shared by start and path.\r | |
410 | i = len(commonprefix([start_list, path_list]))\r | |
411 | \r | |
412 | rel_list = [pardir] * (len(start_list)-i) + path_list[i:]\r | |
413 | if not rel_list:\r | |
414 | return curdir\r | |
415 | return join(*rel_list)\r |