forked from ethanchewy/PythonBuddy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrpath.py
More file actions
233 lines (212 loc) · 7.39 KB
/
rpath.py
File metadata and controls
233 lines (212 loc) · 7.39 KB
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
"""
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.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('/')
def _posix_rnormpath(path):
"""Normalize path, eliminating double slashes, etc."""
slash, 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
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)
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)