Show More
Commit Description:
Merge pull request #17 from nattee/master...
Commit Description:
Merge pull request #17 from nattee/master
upgrade to current working snapshot
References:
File last commit:
Show/Diff file:
Action:
lib/assets/Lib/posixpath.py
| 445 lines
| 13.4 KiB
| text/x-python
| PythonLexer
|
r584 | """Common operations on Posix pathnames. | |||
Instead of importing this module directly, import os and refer to | ||||
this module as os.path. The "os.path" name is an alias for this | ||||
module on Posix systems; on other systems (e.g. Mac, Windows), | ||||
os.path provides the same operations in a manner specific to that | ||||
platform, and is an alias to another module (e.g. macpath, ntpath). | ||||
Some of this can actually be useful on non-Posix systems too, e.g. | ||||
for manipulation of the pathname component of URLs. | ||||
""" | ||||
import os | ||||
import sys | ||||
import stat | ||||
import genericpath | ||||
from genericpath import * | ||||
__all__ = ["normcase","isabs","join","splitdrive","split","splitext", | ||||
"basename","dirname","commonprefix","getsize","getmtime", | ||||
"getatime","getctime","islink","exists","lexists","isdir","isfile", | ||||
"ismount", "expanduser","expandvars","normpath","abspath", | ||||
"samefile","sameopenfile","samestat", | ||||
"curdir","pardir","sep","pathsep","defpath","altsep","extsep", | ||||
"devnull","realpath","supports_unicode_filenames","relpath"] | ||||
# Strings representing various path-related bits and pieces. | ||||
# These are primarily for export; internally, they are hardcoded. | ||||
curdir = '.' | ||||
pardir = '..' | ||||
extsep = '.' | ||||
sep = '/' | ||||
pathsep = ':' | ||||
defpath = ':/bin:/usr/bin' | ||||
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 | ||||
# (another function should be defined to do that). | ||||
def normcase(s): | ||||
"""Normalize case of pathname. Has no effect under Posix""" | ||||
# TODO: on Mac OS X, this should really return s.lower(). | ||||
if not isinstance(s, (bytes, str)): | ||||
raise TypeError("normcase() argument must be str or bytes, " | ||||
"not '{}'".format(s.__class__.__name__)) | ||||
return s | ||||
# Return whether a path is absolute. | ||||
# Trivial in Posix, harder on the Mac or MS-DOS. | ||||
def isabs(s): | ||||
"""Test whether a path is absolute""" | ||||
sep = _get_sep(s) | ||||
return s.startswith(sep) | ||||
# Join pathnames. | ||||
# Ignore the previous parts if a part is absolute. | ||||
# Insert a '/' unless the first part is empty or already ends in '/'. | ||||
def join(a, *p): | ||||
"""Join two or more pathname components, inserting '/' as needed. | ||||
If any component is an absolute path, all previous path components | ||||
will be discarded. An empty last part will result in a path that | ||||
ends with a separator.""" | ||||
sep = _get_sep(a) | ||||
path = a | ||||
try: | ||||
for b in p: | ||||
if b.startswith(sep): | ||||
path = b | ||||
elif not path or path.endswith(sep): | ||||
path += b | ||||
else: | ||||
path += sep + b | ||||
except TypeError: | ||||
valid_types = all(isinstance(s, (str, bytes, bytearray)) | ||||
for s in (a, ) + p) | ||||
if valid_types: | ||||
# Must have a mixture of text and binary data | ||||
raise TypeError("Can't mix strings and bytes in path " | ||||
"components.") from None | ||||
raise | ||||
return path | ||||
# Split a path in head (everything up to the last '/') and tail (the | ||||
# rest). If the path ends in '/', tail will be empty. If there is no | ||||
# '/' in the path, head will be empty. | ||||
# Trailing '/'es are stripped from head unless it is the root. | ||||
def split(p): | ||||
"""Split a pathname. Returns tuple "(head, tail)" where "tail" is | ||||
everything after the final slash. Either part may be empty.""" | ||||
sep = _get_sep(p) | ||||
i = p.rfind(sep) + 1 | ||||
head, tail = p[:i], p[i:] | ||||
if head and head != sep*len(head): | ||||
head = head.rstrip(sep) | ||||
return head, tail | ||||
# Split a path in root and extension. | ||||
# The extension is everything starting at the last dot in the last | ||||
# pathname component; the root is everything before that. | ||||
# It is always true that root + ext == p. | ||||
def splitext(p): | ||||
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 | ||||
# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. | ||||
def splitdrive(p): | ||||
"""Split a pathname into drive and path. On Posix, drive is always | ||||
empty.""" | ||||
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""" | ||||
sep = _get_sep(p) | ||||
i = p.rfind(sep) + 1 | ||||
return p[i:] | ||||
# Return the head (dirname) part of a path, same as split(path)[0]. | ||||
def dirname(p): | ||||
"""Returns the directory component of a pathname""" | ||||
sep = _get_sep(p) | ||||
i = p.rfind(sep) + 1 | ||||
head = p[:i] | ||||
if head and head != sep*len(head): | ||||
head = head.rstrip(sep) | ||||
return head | ||||
# Is a path a symbolic link? | ||||
# This will always return false on systems where os.lstat doesn't exist. | ||||
def islink(path): | ||||
"""Test whether a path is a symbolic link""" | ||||
try: | ||||
st = os.lstat(path) | ||||
except (os.error, AttributeError): | ||||
return False | ||||
return stat.S_ISLNK(st.st_mode) | ||||
# Being true for dangling symbolic links is also useful. | ||||
def lexists(path): | ||||
"""Test whether a path exists. Returns True for broken symbolic links""" | ||||
try: | ||||
os.lstat(path) | ||||
except os.error: | ||||
return False | ||||
return True | ||||
# Are two filenames really pointing to the same file? | ||||
def samefile(f1, f2): | ||||
"""Test whether two pathnames reference the same actual file""" | ||||
s1 = os.stat(f1) | ||||
s2 = os.stat(f2) | ||||
return samestat(s1, s2) | ||||
# Are two open files really referencing the same file? | ||||
# (Not necessarily the same file descriptor!) | ||||
def sameopenfile(fp1, fp2): | ||||
"""Test whether two open file objects reference the same file""" | ||||
s1 = os.fstat(fp1) | ||||
s2 = os.fstat(fp2) | ||||
return samestat(s1, s2) | ||||
# Are two stat buffers (obtained from stat, fstat or lstat) | ||||
# describing the same file? | ||||
def samestat(s1, s2): | ||||
"""Test whether two stat buffers reference the same file""" | ||||
return s1.st_ino == s2.st_ino and \ | ||||
s1.st_dev == s2.st_dev | ||||
# Is a path a mount point? | ||||
# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?) | ||||
def ismount(path): | ||||
"""Test whether a path is a mount point""" | ||||
if islink(path): | ||||
# A symlink can never be a mount point | ||||
return False | ||||
try: | ||||
s1 = os.lstat(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 | ||||
dev2 = s2.st_dev | ||||
if dev1 != dev2: | ||||
return True # path/.. on a different device as path | ||||
ino1 = s1.st_ino | ||||
ino2 = s2.st_ino | ||||
if ino1 == ino2: | ||||
return True # path/.. is the same i-node as path | ||||
return False | ||||
# Expand paths beginning with '~' or '~user'. | ||||
# '~' means $HOME; '~user' means that user's home directory. | ||||
# If the path doesn't begin with '~', or if the user or $HOME is unknown, | ||||
# the path is returned unchanged (leaving error reporting to whatever | ||||
# function is called with the expanded path as argument). | ||||
# See also module 'glob' for expansion of *, ? and [...] in pathnames. | ||||
# (A function should also be defined to do full *sh-style environment | ||||
# variable expansion.) | ||||
def expanduser(path): | ||||
"""Not relevant for Brython, always return path.""" | ||||
return path | ||||
# Expand paths containing shell variable substitutions. | ||||
# This expands the forms $variable and ${variable} only. | ||||
# 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, _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 = search(path, i) | ||||
if not m: | ||||
break | ||||
i, j = m.span(0) | ||||
name = m.group(1) | ||||
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:] | ||||
value = os.environ[name] | ||||
if isinstance(path, bytes): | ||||
value = value.encode('ASCII') | ||||
path = path[:i] + value | ||||
i = len(path) | ||||
path += tail | ||||
else: | ||||
i = j | ||||
return path | ||||
# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. | ||||
# It should be understood that this may change the meaning of the path | ||||
# if it contains symbolic links! | ||||
def normpath(path): | ||||
"""Normalize path, eliminating double slashes, etc.""" | ||||
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(sep*2) and not path.startswith(sep*3)): | ||||
initial_slashes = 2 | ||||
comps = path.split(sep) | ||||
new_comps = [] | ||||
for comp in comps: | ||||
if comp in (empty, dot): | ||||
continue | ||||
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 = sep.join(comps) | ||||
if initial_slashes: | ||||
path = sep*initial_slashes + path | ||||
return path or dot | ||||
def abspath(path): | ||||
"""Return an absolute path.""" | ||||
if not isabs(path): | ||||
if isinstance(path, bytes): | ||||
cwd = os.getcwdb() | ||||
else: | ||||
cwd = os.getcwd() | ||||
path = join(cwd, path) | ||||
return normpath(path) | ||||
# Return a canonical path (i.e. the absolute location of a file on the | ||||
# filesystem). | ||||
def realpath(filename): | ||||
"""Return the canonical path of the specified filename, eliminating any | ||||
symbolic links encountered in the path.""" | ||||
path, ok = _joinrealpath(filename[:0], filename, {}) | ||||
return abspath(path) | ||||
# Join two paths, normalizing ang eliminating any symbolic links | ||||
# encountered in the second path. | ||||
def _joinrealpath(path, rest, seen): | ||||
if isinstance(path, bytes): | ||||
sep = b'/' | ||||
curdir = b'.' | ||||
pardir = b'..' | ||||
else: | ||||
sep = '/' | ||||
curdir = '.' | ||||
pardir = '..' | ||||
if isabs(rest): | ||||
rest = rest[1:] | ||||
path = sep | ||||
while rest: | ||||
name, _, rest = rest.partition(sep) | ||||
if not name or name == curdir: | ||||
# current dir | ||||
continue | ||||
if name == pardir: | ||||
# parent dir | ||||
if path: | ||||
path, name = split(path) | ||||
if name == pardir: | ||||
path = join(path, pardir, pardir) | ||||
else: | ||||
path = pardir | ||||
continue | ||||
newpath = join(path, name) | ||||
if not islink(newpath): | ||||
path = newpath | ||||
continue | ||||
# Resolve the symbolic link | ||||
if newpath in seen: | ||||
# Already seen this path | ||||
path = seen[newpath] | ||||
if path is not None: | ||||
# use cached value | ||||
continue | ||||
# The symlink is not resolved, so we must have a symlink loop. | ||||
# Return already resolved part + rest of the path unchanged. | ||||
return join(newpath, rest), False | ||||
seen[newpath] = None # not resolved symlink | ||||
path, ok = _joinrealpath(path, os.readlink(newpath), seen) | ||||
if not ok: | ||||
return join(path, rest), False | ||||
seen[newpath] = path # resolved symlink | ||||
return path, True | ||||
supports_unicode_filenames = (sys.platform == 'darwin') | ||||
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 = [x for x in abspath(start).split(sep) if x] | ||||
path_list = [x for x in abspath(path).split(sep) if x] | ||||
# Work out how much of the filepath is shared by start and path. | ||||
i = len(commonprefix([start_list, path_list])) | ||||
rel_list = [pardir] * (len(start_list)-i) + path_list[i:] | ||||
if not rel_list: | ||||
return curdir | ||||
return join(*rel_list) | ||||