1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
|
"""
Minimal (and limited) RPython version of some functions contained in os.path.
"""
import os, stat
from rpython.rlib import rposix
from rpython.rlib.signature import signature
from rpython.rlib.rstring import assert_str0
from rpython.annotator.model import s_Str0
# ____________________________________________________________
#
# Generic implementations in RPython for both POSIX and NT
#
def risdir(s):
"""Return true if the pathname refers to an existing directory."""
try:
st = os.stat(s)
except OSError:
return False
return stat.S_ISDIR(st.st_mode)
# ____________________________________________________________
#
# POSIX-only implementations
#
def _posix_risabs(s):
"""Test whether a path is absolute"""
return s.startswith('/')
@signature(s_Str0, returns=s_Str0)
def _posix_rnormpath(path):
"""Normalize path, eliminating double slashes, etc."""
slash, dot = '/', '.'
assert_str0(dot)
if path == '':
return dot
initial_slashes = path.startswith('/')
# 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('///')):
initial_slashes = 2
comps = path.split('/')
new_comps = []
for comp in comps:
if comp == '' or comp == '.':
continue
if (comp != '..' or (not initial_slashes and not new_comps) or
(new_comps and new_comps[-1] == '..')):
new_comps.append(comp)
elif new_comps:
new_comps.pop()
comps = new_comps
path = slash.join(comps)
if initial_slashes:
path = slash*initial_slashes + path
assert_str0(path)
return path or dot
@signature(s_Str0, returns=s_Str0)
def _posix_rabspath(path):
"""Return an absolute, **non-normalized** path.
**This version does not let exceptions propagate.**"""
try:
if not _posix_risabs(path):
cwd = os.getcwd()
path = _posix_rjoin(cwd, path)
assert path is not None
return _posix_rnormpath(path)
except OSError:
return path
def _posix_rjoin(a, b):
"""Join two pathname components, inserting '/' as needed.
If the second component is an absolute path, the first one
will be discarded. An empty last part will result in a path that
ends with a separator."""
path = a
if b.startswith('/'):
path = b
elif path == '' or path.endswith('/'):
path += b
else:
path += '/' + b
return path
# ____________________________________________________________
#
# NT-only implementations
#
def _nt_risabs(s):
"""Test whether a path is absolute"""
s = _nt_rsplitdrive(s)[1]
return s.startswith('/') or s.startswith('\\')
def _nt_rnormpath(path):
"""Normalize path, eliminating double slashes, etc."""
backslash, dot = '\\', '.'
if path.startswith(('\\\\.\\', '\\\\?\\')):
# in the case of paths with these prefixes:
# \\.\ -> device names
# \\?\ -> literal paths
# do not do any normalization, but return the path unchanged
return path
path = path.replace("/", "\\")
prefix, path = _nt_rsplitdrive(path)
# We need to be careful here. If the prefix is empty, and the path starts
# with a backslash, it could either be an absolute path on the current
# drive (\dir1\dir2\file) or a UNC filename (\\server\mount\dir1\file). It
# is therefore imperative NOT to collapse multiple backslashes blindly in
# that case.
# The code below preserves multiple backslashes when there is no drive
# letter. This means that the invalid filename \\\a\b is preserved
# unchanged, where a\\\b is normalised to a\b. It's not clear that there
# is any better behaviour for such edge cases.
if prefix == '':
# No drive letter - preserve initial backslashes
while path.startswith("\\"):
prefix = prefix + backslash
path = path[1:]
else:
# We have a drive letter - collapse initial backslashes
if path.startswith("\\"):
prefix = prefix + backslash
path = path.lstrip("\\")
comps = path.split("\\")
i = 0
while i < len(comps):
if comps[i] in ('.', ''):
del comps[i]
elif comps[i] == '..':
if i > 0 and comps[i-1] != '..':
del comps[i-1:i+1]
i -= 1
elif i == 0 and prefix.endswith("\\"):
del comps[i]
else:
i += 1
else:
i += 1
# If the path is now empty, substitute '.'
if not prefix and not comps:
comps.append(dot)
return prefix + backslash.join(comps)
@signature(s_Str0, returns=s_Str0)
def _nt_rabspath(path):
try:
if path == '':
path = os.getcwd()
return rposix.getfullpathname(path)
except OSError:
return path
def _nt_rsplitdrive(p):
"""Split a pathname into drive/UNC sharepoint and relative path
specifiers.
Returns a 2-tuple (drive_or_unc, path); either part may be empty.
"""
if len(p) > 1:
normp = p.replace(altsep, sep)
if normp.startswith('\\\\') and not normp.startswith('\\\\\\'):
# is a UNC path:
# vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
# \\machine\mountpoint\directory\etc\...
# directory ^^^^^^^^^^^^^^^
index = normp.find('\\', 2)
if index < 0:
return '', p
index2 = normp.find('\\', index + 1)
# a UNC path can't have two slashes in a row
# (after the initial two)
if index2 == index + 1:
return '', p
if index2 < 0:
index2 = len(p)
return p[:index2], p[index2:]
if normp[1] == ':':
return p[:2], p[2:]
return '', p
def _nt_rjoin(path, p):
"""Join two or more pathname components, inserting "\\" as needed."""
result_drive, result_path = _nt_rsplitdrive(path)
p_drive, p_path = _nt_rsplitdrive(p)
p_is_rel = True
if p_path and p_path[0] in '\\/':
# Second path is absolute
if p_drive or not result_drive:
result_drive = p_drive
result_path = p_path
p_is_rel = False
elif p_drive and p_drive != result_drive:
if p_drive.lower() != result_drive.lower():
# Different drives => ignore the first path entirely
result_drive = p_drive
result_path = p_path
p_is_rel = False
else:
# Same drive in different case
result_drive = p_drive
if p_is_rel:
# Second path is relative to the first
if result_path and result_path[-1] not in '\\/':
result_path = result_path + '\\'
result_path = result_path + p_path
## add separator between UNC and non-absolute path
if (result_path and result_path[0] not in '\\/' and
result_drive and result_drive[-1] != ':'):
return result_drive + '\\' + result_path
return result_drive + result_path
# ____________________________________________________________
if os.name == 'posix':
sep = altsep = '/'
risabs = _posix_risabs
rnormpath = _posix_rnormpath
rabspath = _posix_rabspath
rjoin = _posix_rjoin
elif os.name == 'nt':
sep, altsep = '\\', '/'
risabs = _nt_risabs
rnormpath = _nt_rnormpath
rabspath = _nt_rabspath
rsplitdrive = _nt_rsplitdrive
rjoin = _nt_rjoin
else:
raise ImportError('Unsupported os: %s' % os.name)
|