Index: Lib/posixpath.py |
=================================================================== |
--- Lib/posixpath.py (revision 66736) |
+++ Lib/posixpath.py (working copy) |
@@ -11,6 +11,7 @@ |
""" |
import os |
+import sys |
import stat |
import genericpath |
from genericpath import * |
@@ -23,7 +24,8 @@ |
"curdir","pardir","sep","pathsep","defpath","altsep","extsep", |
"devnull","realpath","supports_unicode_filenames","relpath"] |
-# strings representing various path-related bits and pieces |
+# Strings representing various path-related bits and pieces. |
+# These are primarily for export; internally, they are hardcoded. |
curdir = '.' |
pardir = '..' |
extsep = '.' |
@@ -33,6 +35,12 @@ |
altsep = None |
devnull = '/dev/null' |
+def _get_sep(path): |
+ if isinstance(path, bytes): |
+ return b'/' |
+ else: |
+ return '/' |
+ |
# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. |
# On MS-DOS this may also turn slashes into backslashes; however, other |
# normalizations (such as optimizing '../' away) are not allowed |
@@ -40,6 +48,7 @@ |
def normcase(s): |
"""Normalize case of pathname. Has no effect under Posix""" |
+ # TODO: on Mac OS X, this should really return s.lower(). |
return s |
@@ -48,7 +57,8 @@ |
def isabs(s): |
"""Test whether a path is absolute""" |
- return s.startswith('/') |
+ sep = _get_sep(s) |
+ return s.startswith(sep) |
# Join pathnames. |
@@ -59,14 +69,15 @@ |
"""Join two or more pathname components, inserting '/' as needed. |
If any component is an absolute path, all previous path components |
will be discarded.""" |
+ sep = _get_sep(a) |
path = a |
for b in p: |
- if b.startswith('/'): |
+ if b.startswith(sep): |
path = b |
- elif path == '' or path.endswith('/'): |
+ elif not path or path.endswith(sep): |
path += b |
else: |
- path += '/' + b |
+ path += sep + b |
return path |
@@ -78,10 +89,11 @@ |
def split(p): |
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is |
everything after the final slash. Either part may be empty.""" |
- i = p.rfind('/') + 1 |
+ sep = _get_sep(p) |
+ i = p.rfind(sep) + 1 |
head, tail = p[:i], p[i:] |
- if head and head != '/'*len(head): |
- head = head.rstrip('/') |
+ if head and head != sep*len(head): |
+ head = head.rstrip(sep) |
return head, tail |
@@ -91,7 +103,13 @@ |
# It is always true that root + ext == p. |
def splitext(p): |
- return genericpath._splitext(p, sep, altsep, extsep) |
+ if isinstance(p, bytes): |
+ sep = b'/' |
+ extsep = b'.' |
+ else: |
+ sep = '/' |
+ extsep = '.' |
+ return genericpath._splitext(p, sep, None, extsep) |
splitext.__doc__ = genericpath._splitext.__doc__ |
# Split a pathname into a drive specification and the rest of the |
@@ -100,14 +118,15 @@ |
def splitdrive(p): |
"""Split a pathname into drive and path. On Posix, drive is always |
empty.""" |
- return '', p |
+ return p[:0], p |
# Return the tail (basename) part of a path, same as split(path)[1]. |
def basename(p): |
"""Returns the final component of a pathname""" |
- i = p.rfind('/') + 1 |
+ sep = _get_sep(p) |
+ i = p.rfind(sep) + 1 |
return p[i:] |
@@ -115,10 +134,11 @@ |
def dirname(p): |
"""Returns the directory component of a pathname""" |
- i = p.rfind('/') + 1 |
+ sep = _get_sep(p) |
+ i = p.rfind(sep) + 1 |
head = p[:i] |
- if head and head != '/'*len(head): |
- head = head.rstrip('/') |
+ if head and head != sep*len(head): |
+ head = head.rstrip(sep) |
return head |
@@ -179,7 +199,11 @@ |
"""Test whether a path is a mount point""" |
try: |
s1 = os.lstat(path) |
- s2 = os.lstat(join(path, '..')) |
+ if isinstance(path, bytes): |
+ parent = join(path, b'..') |
+ else: |
+ parent = join(path, '..') |
+ s2 = os.lstat(parent) |
except os.error: |
return False # It doesn't exist -- so not a mount point :-) |
dev1 = s1.st_dev |
@@ -205,9 +229,14 @@ |
def expanduser(path): |
"""Expand ~ and ~user constructions. If user or $HOME is unknown, |
do nothing.""" |
- if not path.startswith('~'): |
+ if isinstance(path, bytes): |
+ tilde = b'~' |
+ else: |
+ tilde = '~' |
+ if not path.startswith(tilde): |
return path |
- i = path.find('/', 1) |
+ sep = _get_sep(path) |
+ i = path.find(sep, 1) |
if i < 0: |
i = len(path) |
if i == 1: |
@@ -218,12 +247,17 @@ |
userhome = os.environ['HOME'] |
else: |
import pwd |
+ name = path[1:i] |
+ if isinstance(name, bytes): |
+ name = str(name, 'ASCII') |
try: |
- pwent = pwd.getpwnam(path[1:i]) |
+ pwent = pwd.getpwnam(name) |
except KeyError: |
return path |
userhome = pwent.pw_dir |
- userhome = userhome.rstrip('/') |
+ if isinstance(path, bytes): |
+ userhome = userhome.encode(sys.getfilesystemencoding()) |
+ userhome = userhome.rstrip(sep) |
return userhome + path[i:] |
@@ -232,28 +266,47 @@ |
# Non-existent variables are left unchanged. |
_varprog = None |
+_varprogb = None |
def expandvars(path): |
"""Expand shell variables of form $var and ${var}. Unknown variables |
are left unchanged.""" |
- global _varprog |
- if '$' not in path: |
- return path |
- if not _varprog: |
- import re |
- _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) |
+ global _varprog, _varprogb |
+ if isinstance(path, bytes): |
+ if b'$' not in path: |
+ return path |
+ if not _varprogb: |
+ import re |
+ _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII) |
+ search = _varprogb.search |
+ start = b'{' |
+ end = b'}' |
+ else: |
+ if '$' not in path: |
+ return path |
+ if not _varprog: |
+ import re |
+ _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) |
+ search = _varprog.search |
+ start = '{' |
+ end = '}' |
i = 0 |
while True: |
- m = _varprog.search(path, i) |
+ m = search(path, i) |
if not m: |
break |
i, j = m.span(0) |
name = m.group(1) |
- if name.startswith('{') and name.endswith('}'): |
+ if name.startswith(start) and name.endswith(end): |
name = name[1:-1] |
+ if isinstance(name, bytes): |
+ name = str(name, 'ASCII') |
if name in os.environ: |
tail = path[j:] |
- path = path[:i] + os.environ[name] |
+ value = os.environ[name] |
+ if isinstance(path, bytes): |
+ value = value.encode('ASCII') |
+ path = path[:i] + value |
i = len(path) |
path += tail |
else: |
@@ -267,35 +320,49 @@ |
def normpath(path): |
"""Normalize path, eliminating double slashes, etc.""" |
- if path == '': |
- return '.' |
- initial_slashes = path.startswith('/') |
+ if isinstance(path, bytes): |
+ sep = b'/' |
+ empty = b'' |
+ dot = b'.' |
+ dotdot = b'..' |
+ else: |
+ sep = '/' |
+ empty = '' |
+ dot = '.' |
+ dotdot = '..' |
+ if path == empty: |
+ return dot |
+ initial_slashes = path.startswith(sep) |
# POSIX allows one or two initial slashes, but treats three or more |
# as single slash. |
if (initial_slashes and |
- path.startswith('//') and not path.startswith('///')): |
+ path.startswith(sep*2) and not path.startswith(sep*3)): |
initial_slashes = 2 |
- comps = path.split('/') |
+ comps = path.split(sep) |
new_comps = [] |
for comp in comps: |
- if comp in ('', '.'): |
+ if comp in (empty, dot): |
continue |
- if (comp != '..' or (not initial_slashes and not new_comps) or |
- (new_comps and new_comps[-1] == '..')): |
+ if (comp != dotdot or (not initial_slashes and not new_comps) or |
+ (new_comps and new_comps[-1] == dotdot)): |
new_comps.append(comp) |
elif new_comps: |
new_comps.pop() |
comps = new_comps |
- path = '/'.join(comps) |
+ path = sep.join(comps) |
if initial_slashes: |
- path = '/'*initial_slashes + path |
- return path or '.' |
+ path = sep*initial_slashes + path |
+ return path or dot |
def abspath(path): |
"""Return an absolute path.""" |
if not isabs(path): |
- path = join(os.getcwd(), path) |
+ if isinstance(path, bytes): |
+ cwd = os.getcwdb() |
+ else: |
+ cwd = os.getcwd() |
+ path = join(cwd, path) |
return normpath(path) |
@@ -305,10 +372,16 @@ |
def realpath(filename): |
"""Return the canonical path of the specified filename, eliminating any |
symbolic links encountered in the path.""" |
+ if isinstance(filename, bytes): |
+ sep = b'/' |
+ empty = b'' |
+ else: |
+ sep = '/' |
+ empty = '' |
if isabs(filename): |
- bits = ['/'] + filename.split('/')[1:] |
+ bits = [sep] + filename.split(sep)[1:] |
else: |
- bits = [''] + filename.split('/') |
+ bits = [empty] + filename.split(sep) |
for i in range(2, len(bits)+1): |
component = join(*bits[0:i]) |
@@ -347,12 +420,24 @@ |
supports_unicode_filenames = False |
-def relpath(path, start=curdir): |
+def relpath(path, start=None): |
"""Return a relative version of a path""" |
if not path: |
raise ValueError("no path specified") |
+ if isinstance(path, bytes): |
+ curdir = b'.' |
+ sep = b'/' |
+ pardir = b'..' |
+ else: |
+ curdir = '.' |
+ sep = '/' |
+ pardir = '..' |
+ |
+ if start is None: |
+ start = curdir |
+ |
start_list = abspath(start).split(sep) |
path_list = abspath(path).split(sep) |