Index: Modules/posixmodule.c =================================================================== --- Modules/posixmodule.c (révision 66736) +++ Modules/posixmodule.c (copie de travail) @@ -1968,63 +1968,18 @@ #ifdef HAVE_GETCWD -PyDoc_STRVAR(posix_getcwd__doc__, -"getcwd() -> path\n\n\ -Return a string representing the current working directory."); - -static PyObject * -posix_getcwd(PyObject *self, PyObject *noargs) -{ - int bufsize_incr = 1024; - int bufsize = 0; - char *tmpbuf = NULL; - char *res = NULL; - PyObject *dynamic_return; - - Py_BEGIN_ALLOW_THREADS - do { - bufsize = bufsize + bufsize_incr; - tmpbuf = malloc(bufsize); - if (tmpbuf == NULL) { - break; - } -#if defined(PYOS_OS2) && defined(PYCC_GCC) - res = _getcwd2(tmpbuf, bufsize); -#else - res = getcwd(tmpbuf, bufsize); -#endif - - if (res == NULL) { - free(tmpbuf); - } - } while ((res == NULL) && (errno == ERANGE)); - Py_END_ALLOW_THREADS - - if (res == NULL) - return posix_error(); - - dynamic_return = PyUnicode_FromString(tmpbuf); - free(tmpbuf); - - return dynamic_return; -} - -PyDoc_STRVAR(posix_getcwdu__doc__, -"getcwdu() -> path\n\n\ -Return a unicode string representing the current working directory."); - static PyObject * -posix_getcwdu(PyObject *self, PyObject *noargs) +posix_getcwd(int use_bytes) { char buf[1026]; char *res; #ifdef Py_WIN_WIDE_FILENAMES - DWORD len; - if (unicode_file_names()) { + if (!use_bytes && unicode_file_names()) { wchar_t wbuf[1026]; wchar_t *wbuf2 = wbuf; PyObject *resobj; + DWORD len; Py_BEGIN_ALLOW_THREADS len = GetCurrentDirectoryW(sizeof wbuf/ sizeof wbuf[0], wbuf); /* If the buffer is large enough, len does not include the @@ -2059,8 +2014,30 @@ Py_END_ALLOW_THREADS if (res == NULL) return posix_error(); + if (use_bytes) + return PyBytes_FromStringAndSize(buf, strlen(buf)); return PyUnicode_Decode(buf, strlen(buf), Py_FileSystemDefaultEncoding,"strict"); } + +PyDoc_STRVAR(posix_getcwd__doc__, +"getcwd() -> path\n\n\ +Return a unicode string representing the current working directory."); + +static PyObject * +posix_getcwd_unicode(PyObject *self) +{ + return posix_getcwd(0); +} + +PyDoc_STRVAR(posix_getcwdb__doc__, +"getcwdb() -> path\n\n\ +Return a bytes string representing the current working directory."); + +static PyObject * +posix_getcwd_bytes(PyObject *self) +{ + return posix_getcwd(1); +} #endif @@ -2378,9 +2355,12 @@ v = w; } else { - /* fall back to the original byte string, as - discussed in patch #683592 */ + /* Ignore undecodable filenames, as discussed + * in issue 3187. To include these, + * use getcwdb(). */ PyErr_Clear(); + Py_DECREF(v); + continue; } } if (PyList_Append(d, v) != 0) { @@ -4477,9 +4457,7 @@ v = w; } else { - /* fall back to the original byte string, as - discussed in patch #683592 */ - PyErr_Clear(); + v = NULL; } } return v; @@ -6810,8 +6788,10 @@ {"ctermid", posix_ctermid, METH_NOARGS, posix_ctermid__doc__}, #endif #ifdef HAVE_GETCWD - {"getcwd", posix_getcwd, METH_NOARGS, posix_getcwd__doc__}, - {"getcwdu", posix_getcwdu, METH_NOARGS, posix_getcwdu__doc__}, + {"getcwd", (PyCFunction)posix_getcwd_unicode, + METH_NOARGS, posix_getcwd__doc__}, + {"getcwdb", (PyCFunction)posix_getcwd_bytes, + METH_NOARGS, posix_getcwdb__doc__}, #endif #ifdef HAVE_LINK {"link", posix_link, METH_VARARGS, posix_link__doc__}, Index: Lib/posixpath.py =================================================================== --- Lib/posixpath.py (révision 66736) +++ Lib/posixpath.py (copie de travail) @@ -14,6 +14,7 @@ import stat import genericpath from genericpath import * +import sys __all__ = ["normcase","isabs","join","splitdrive","split","splitext", "basename","dirname","commonprefix","getsize","getmtime", @@ -33,6 +34,12 @@ altsep = None devnull = '/dev/null' +def _get_sep(path): + if isinstance(path, (bytes, bytearray)): + 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 @@ -48,7 +55,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 +67,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 +87,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 @@ -100,14 +110,18 @@ def splitdrive(p): """Split a pathname into drive and path. On Posix, drive is always empty.""" - return '', p + if isinstance(p, (bytes, bytearray)): + return b'', p + else: + return '', 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 +129,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 +194,11 @@ """Test whether a path is a mount point""" try: s1 = os.lstat(path) - s2 = os.lstat(join(path, '..')) + if isinstance(path, (bytes, bytearray)): + 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 +224,14 @@ def expanduser(path): """Expand ~ and ~user constructions. If user or $HOME is unknown, do nothing.""" - if not path.startswith('~'): + if isinstance(path, (bytes, bytearray)): + 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 +242,17 @@ userhome = os.environ['HOME'] else: import pwd + name = path[1:i] + if isinstance(name, (bytes, bytearray)): + 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, bytearray)): + userhome = userhome.encode(sys.getfilesystemencoding()) + userhome = userhome.rstrip(sep) return userhome + path[i:] @@ -232,28 +261,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, bytearray)): + 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, bytearray)): + 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, bytearray)): + value = value.encode('ASCII') + path = path[:i] + value i = len(path) path += tail else: @@ -267,35 +315,49 @@ def normpath(path): """Normalize path, eliminating double slashes, etc.""" - if path == '': - return '.' - initial_slashes = path.startswith('/') + if isinstance(path, (bytes, bytearray)): + 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, bytearray)): + cwd = os.getcwdb() + else: + cwd = os.getcwd() + path = join(cwd, path) return normpath(path) @@ -305,10 +367,16 @@ def realpath(filename): """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path.""" + if isinstance(filename, (bytes, bytearray)): + 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 +415,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, bytearray)): + 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) Index: Lib/glob.py =================================================================== --- Lib/glob.py (révision 66736) +++ Lib/glob.py (copie de travail) @@ -27,7 +27,7 @@ return dirname, basename = os.path.split(pathname) if not dirname: - for name in glob1(os.curdir, basename): + for name in glob1(None, basename): yield name return if has_magic(dirname): @@ -48,10 +48,10 @@ def glob1(dirname, pattern): if not dirname: - dirname = os.curdir - if isinstance(pattern, str) and not isinstance(dirname, str): - dirname = str(dirname, sys.getfilesystemencoding() or - sys.getdefaultencoding()) + if isinstance(pattern, (bytes, bytearray)): + dirname = bytes(os.curdir, 'ASCII') + else: + dirname = os.curdir try: names = os.listdir(dirname) except os.error: @@ -73,6 +73,11 @@ magic_check = re.compile('[*?[]') +magic_check_bytes = re.compile(b'[*?[]') def has_magic(s): - return magic_check.search(s) is not None + if isinstance(s, (bytes, bytearray)): + match = magic_check_bytes.search(s) + else: + match = magic_check.search(s) + return match is not None Index: Lib/fnmatch.py =================================================================== --- Lib/fnmatch.py (révision 66736) +++ Lib/fnmatch.py (copie de travail) @@ -37,15 +37,23 @@ pat = os.path.normcase(pat) return fnmatchcase(name, pat) +def _compile_pattern(pat): + if not pat in _cache: + if isinstance(pat, bytes): + pat_str = str(pat, 'ISO-8859-1') + res_str = translate(pat_str) + res = res_str.encode('ISO-8859-1') + else: + res = translate(pat) + _cache[pat] = re.compile(res) + return _cache[pat].match + def filter(names, pat): """Return the subset of the list NAMES that match PAT""" import os,posixpath result=[] pat=os.path.normcase(pat) - if not pat in _cache: - res = translate(pat) - _cache[pat] = re.compile(res) - match=_cache[pat].match + match = _compile_pattern(pat) if os.path is posixpath: # normcase on posix is NOP. Optimize it away from the loop. for name in names: @@ -64,10 +72,8 @@ its arguments. """ - if not pat in _cache: - res = translate(pat) - _cache[pat] = re.compile(res) - return _cache[pat].match(name) is not None + match = _compile_pattern(pat) + return match(name) is not None def translate(pat): """Translate a shell PATTERN to a regular expression. Index: Lib/io.py =================================================================== --- Lib/io.py (révision 66736) +++ Lib/io.py (copie de travail) @@ -180,7 +180,7 @@ opened in a text mode, and for bytes a BytesIO can be used like a file opened in a binary mode. """ - if not isinstance(file, (str, int)): + if not isinstance(file, (str, bytes, int)): raise TypeError("invalid file: %r" % file) if not isinstance(mode, str): raise TypeError("invalid mode: %r" % mode) Index: Lib/test/test_fnmatch.py =================================================================== --- Lib/test/test_fnmatch.py (révision 66736) +++ Lib/test/test_fnmatch.py (copie de travail) @@ -37,7 +37,16 @@ check('a', r'[!\]') check('\\', r'[!\]', 0) + def test_mix_bytes_str(self): + self.assertRaises(TypeError, fnmatch, 'test', b'*') + self.assertRaises(TypeError, fnmatch, b'test', '*') + self.assertRaises(TypeError, fnmatchcase, 'test', b'*') + self.assertRaises(TypeError, fnmatchcase, b'test', '*') + def test_bytes(self): + self.check_match(b'test', b'te*') + self.check_match(b'test\xff', b'te*\xff') + def test_main(): support.run_unittest(FnmatchTestCase) Index: Lib/test/test_unicode_file.py =================================================================== --- Lib/test/test_unicode_file.py (révision 66736) +++ Lib/test/test_unicode_file.py (copie de travail) @@ -90,7 +90,7 @@ os.unlink(filename1 + ".new") def _do_directory(self, make_name, chdir_name, encoded): - cwd = os.getcwd() + cwd = os.getcwdb() if os.path.isdir(make_name): os.rmdir(make_name) os.mkdir(make_name) @@ -98,10 +98,10 @@ os.chdir(chdir_name) try: if not encoded: - cwd_result = os.getcwdu() + cwd_result = os.getcwd() name_result = make_name else: - cwd_result = os.getcwd().decode(TESTFN_ENCODING) + cwd_result = os.getcwdb().decode(TESTFN_ENCODING) name_result = make_name.decode(TESTFN_ENCODING) cwd_result = unicodedata.normalize("NFD", cwd_result) Index: Lib/test/test_posix.py =================================================================== --- Lib/test/test_posix.py (révision 66736) +++ Lib/test/test_posix.py (copie de travail) @@ -29,7 +29,7 @@ def testNoArgFunctions(self): # test posix functions which take no arguments and have # no side-effects which we need to cleanup (e.g., fork, wait, abort) - NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdu", "uname", + NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdb", "uname", "times", "getloadavg", "getegid", "geteuid", "getgid", "getgroups", "getpid", "getpgrp", "getppid", "getuid", Index: Lib/test/test_posixpath.py =================================================================== --- Lib/test/test_posixpath.py (révision 66736) +++ Lib/test/test_posixpath.py (copie de travail) @@ -31,9 +31,11 @@ def test_normcase(self): # Check that normcase() is idempotent p = "FoO/./BaR" - p = posixpath.normcase(p) self.assertEqual(p, posixpath.normcase(p)) + p = b"FoO/./BaR" + self.assertEqual(p, posixpath.normcase(p)) + self.assertRaises(TypeError, posixpath.normcase) def test_join(self): @@ -41,10 +43,20 @@ self.assertEqual(posixpath.join("/foo", "bar", "baz"), "/foo/bar/baz") self.assertEqual(posixpath.join("/foo/", "bar/", "baz/"), "/foo/bar/baz/") + self.assertEqual(posixpath.join(b"/foo", b"bar", b"/bar", b"baz"), + b"/bar/baz") + self.assertEqual(posixpath.join(b"/foo", b"bar", b"baz"), + b"/foo/bar/baz") + self.assertEqual(posixpath.join(b"/foo/", b"bar/", b"baz/"), + b"/foo/bar/baz/") + self.assertRaises(TypeError, posixpath.join) + self.assertRaises(TypeError, posixpath.join, b"bytes", "str") + self.assertRaises(TypeError, posixpath.join, "str", b"bytes") def test_splitdrive(self): self.assertEqual(posixpath.splitdrive("/foo/bar"), ("", "/foo/bar")) + self.assertEqual(posixpath.splitdrive(b"/foo/bar"), (b"", b"/foo/bar")) self.assertRaises(TypeError, posixpath.splitdrive) @@ -55,6 +67,12 @@ self.assertEqual(posixpath.split("////foo"), ("////", "foo")) self.assertEqual(posixpath.split("//foo//bar"), ("//foo", "bar")) + self.assertEqual(posixpath.split(b"/foo/bar"), (b"/foo", b"bar")) + self.assertEqual(posixpath.split(b"/"), (b"/", b"")) + self.assertEqual(posixpath.split(b"foo"), (b"", b"foo")) + self.assertEqual(posixpath.split(b"////foo"), (b"////", b"foo")) + self.assertEqual(posixpath.split(b"//foo//bar"), (b"//foo", b"bar")) + self.assertRaises(TypeError, posixpath.split) def splitextTest(self, path, filename, ext): @@ -87,13 +105,14 @@ self.assertIs(posixpath.isabs("/foo/bar"), True) self.assertIs(posixpath.isabs("foo/bar"), False) + self.assertIs(posixpath.isabs(b""), False) + self.assertIs(posixpath.isabs(b"/"), True) + self.assertIs(posixpath.isabs(b"/foo"), True) + self.assertIs(posixpath.isabs(b"/foo/bar"), True) + self.assertIs(posixpath.isabs(b"foo/bar"), False) + self.assertRaises(TypeError, posixpath.isabs) - def test_splitdrive(self): - self.assertEqual(posixpath.splitdrive("/foo/bar"), ("", "/foo/bar")) - - self.assertRaises(TypeError, posixpath.splitdrive) - def test_basename(self): self.assertEqual(posixpath.basename("/foo/bar"), "bar") self.assertEqual(posixpath.basename("/"), "") @@ -101,6 +120,12 @@ self.assertEqual(posixpath.basename("////foo"), "foo") self.assertEqual(posixpath.basename("//foo//bar"), "bar") + self.assertEqual(posixpath.basename(b"/foo/bar"), b"bar") + self.assertEqual(posixpath.basename(b"/"), b"") + self.assertEqual(posixpath.basename(b"foo"), b"foo") + self.assertEqual(posixpath.basename(b"////foo"), b"foo") + self.assertEqual(posixpath.basename(b"//foo//bar"), b"bar") + self.assertRaises(TypeError, posixpath.basename) def test_dirname(self): @@ -110,6 +135,12 @@ self.assertEqual(posixpath.dirname("////foo"), "////") self.assertEqual(posixpath.dirname("//foo//bar"), "//foo") + self.assertEqual(posixpath.dirname(b"/foo/bar"), b"/foo") + self.assertEqual(posixpath.dirname(b"/"), b"/") + self.assertEqual(posixpath.dirname(b"foo"), b"") + self.assertEqual(posixpath.dirname(b"////foo"), b"////") + self.assertEqual(posixpath.dirname(b"//foo//bar"), b"//foo") + self.assertRaises(TypeError, posixpath.dirname) def test_commonprefix(self): @@ -130,6 +161,19 @@ "/home/swen/spam" ) + self.assertEqual( + posixpath.commonprefix([b"/home/swenson/spam", b"/home/swen/spam"]), + b"/home/swen" + ) + self.assertEqual( + posixpath.commonprefix([b"/home/swen/spam", b"/home/swen/eggs"]), + b"/home/swen/" + ) + self.assertEqual( + posixpath.commonprefix([b"/home/swen/spam", b"/home/swen/spam"]), + b"/home/swen/spam" + ) + testlist = ['', 'abc', 'Xbcd', 'Xb', 'XY', 'abcd', 'aXc', 'abd', 'ab', 'aX', 'abcX'] for s1 in testlist: for s2 in testlist: @@ -330,20 +374,28 @@ def test_expanduser(self): self.assertEqual(posixpath.expanduser("foo"), "foo") + self.assertEqual(posixpath.expanduser(b"foo"), b"foo") try: import pwd except ImportError: pass else: self.assert_(isinstance(posixpath.expanduser("~/"), str)) + self.assert_(isinstance(posixpath.expanduser(b"~/"), bytes)) # if home directory == root directory, this test makes no sense if posixpath.expanduser("~") != '/': self.assertEqual( posixpath.expanduser("~") + "/", posixpath.expanduser("~/") ) + self.assertEqual( + posixpath.expanduser(b"~") + b"/", + posixpath.expanduser(b"~/") + ) self.assert_(isinstance(posixpath.expanduser("~root/"), str)) self.assert_(isinstance(posixpath.expanduser("~foo/"), str)) + self.assert_(isinstance(posixpath.expanduser(b"~root/"), bytes)) + self.assert_(isinstance(posixpath.expanduser(b"~foo/"), bytes)) self.assertRaises(TypeError, posixpath.expanduser) @@ -366,6 +418,19 @@ self.assertEqual(posixpath.expandvars("${{foo}}"), "baz1}") self.assertEqual(posixpath.expandvars("$foo$foo"), "barbar") self.assertEqual(posixpath.expandvars("$bar$bar"), "$bar$bar") + + self.assertEqual(posixpath.expandvars(b"foo"), b"foo") + self.assertEqual(posixpath.expandvars(b"$foo bar"), b"bar bar") + self.assertEqual(posixpath.expandvars(b"${foo}bar"), b"barbar") + self.assertEqual(posixpath.expandvars(b"$[foo]bar"), b"$[foo]bar") + self.assertEqual(posixpath.expandvars(b"$bar bar"), b"$bar bar") + self.assertEqual(posixpath.expandvars(b"$?bar"), b"$?bar") + self.assertEqual(posixpath.expandvars(b"${foo}bar"), b"barbar") + self.assertEqual(posixpath.expandvars(b"$foo}bar"), b"bar}bar") + self.assertEqual(posixpath.expandvars(b"${foo"), b"${foo") + self.assertEqual(posixpath.expandvars(b"${{foo}}"), b"baz1}") + self.assertEqual(posixpath.expandvars(b"$foo$foo"), b"barbar") + self.assertEqual(posixpath.expandvars(b"$bar$bar"), b"$bar$bar") finally: os.environ.clear() os.environ.update(oldenv) @@ -381,15 +446,25 @@ self.assertEqual(posixpath.normpath("///foo/.//bar//.//..//.//baz"), "/foo/baz") self.assertEqual(posixpath.normpath("///..//./foo/.//bar"), "/foo/bar") + self.assertEqual(posixpath.normpath(b""), b".") + self.assertEqual(posixpath.normpath(b"/"), b"/") + self.assertEqual(posixpath.normpath(b"//"), b"//") + self.assertEqual(posixpath.normpath(b"///"), b"/") + self.assertEqual(posixpath.normpath(b"///foo/.//bar//"), b"/foo/bar") + self.assertEqual(posixpath.normpath(b"///foo/.//bar//.//..//.//baz"), b"/foo/baz") + self.assertEqual(posixpath.normpath(b"///..//./foo/.//bar"), b"/foo/bar") + self.assertRaises(TypeError, posixpath.normpath) def test_abspath(self): self.assert_("foo" in posixpath.abspath("foo")) + self.assert_(b"foo" in posixpath.abspath(b"foo")) self.assertRaises(TypeError, posixpath.abspath) def test_realpath(self): self.assert_("foo" in realpath("foo")) + self.assert_(b"foo" in realpath(b"foo")) self.assertRaises(TypeError, posixpath.realpath) if hasattr(os, "symlink"): @@ -505,6 +580,27 @@ finally: os.getcwd = real_getcwd + def test_relpath_bytes(self): + (real_getcwdb, os.getcwdb) = (os.getcwdb, lambda: br"/home/user/bar") + try: + curdir = os.path.split(os.getcwdb())[-1] + self.assertRaises(ValueError, posixpath.relpath, b"") + self.assertEqual(posixpath.relpath(b"a"), b"a") + self.assertEqual(posixpath.relpath(posixpath.abspath(b"a")), b"a") + self.assertEqual(posixpath.relpath(b"a/b"), b"a/b") + self.assertEqual(posixpath.relpath(b"../a/b"), b"../a/b") + self.assertEqual(posixpath.relpath(b"a", b"../b"), + b"../"+curdir+b"/a") + self.assertEqual(posixpath.relpath(b"a/b", b"../c"), + b"../"+curdir+b"/a/b") + self.assertEqual(posixpath.relpath(b"a", b"b/c"), b"../../a") + self.assertEqual(posixpath.relpath(b"a", b"a"), b".") + + self.assertRaises(TypeError, posixpath.relpath, b"bytes", "str") + self.assertRaises(TypeError, posixpath.relpath, "str", b"bytes") + finally: + os.getcwdb = real_getcwdb + def test_main(): support.run_unittest(PosixPathTest)