FFFF Issue #15275: Clean up and speed up the ntpath module. · python/cpython@8518b79 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8518b79

Browse files
Issue #15275: Clean up and speed up the ntpath module.
1 parent f886697 commit 8518b79

File tree

2 files changed

+79
-93
lines changed

2 files changed

+79
-93
lines changed

Lib/ntpath.py

Lines changed: 77 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -32,48 +32,12 @@
3232
defpath = '\\Windows'
3333
devnull = 'nul'
3434

35-
def _get_empty(path):
36-
if isinstance(path, bytes):
37-
return b''
38-
else:
39-
return ''
40-
41-
def _get_sep(path):
42-
if isinstance(path, bytes):
43-
return b'\\'
44-
else:
45-
return '\\'
46-
47-
def _get_altsep(path):
48-
if isinstance(path, bytes):
49-
return b'/'
50-
else:
51-
return '/'
52-
5335
def _get_bothseps(path):
5436
if isinstance(path, bytes):
5537
return b'\\/'
5638
else:
5739
return '\\/'
5840

59-
def _get_dot(path):
60-
if isinstance(path, bytes):
61-
return b'.'
62-
else:
63-
return '.'
64-
65-
def _get_colon(path):
66-
if isinstance(path, bytes):
67-
return b':'
68-
else:
69-
return ':'
70-
71-
def _get_special(path):
72-
if isinstance(path, bytes):
73-
return (b'\\\\.\\', b'\\\\?\\')
74-
else:
75-
return ('\\\\.\\', '\\\\?\\')
76-
7741
# Normalize the case of a pathname and map slashes to backslashes.
7842
# Other normalizations (such as optimizing '../' away) are not done
7943
# (this is done by normpath).
@@ -82,10 +46,16 @@ def normcase(s):
8246
"""Normalize case of pathname.
8347
8448
Makes all characters lowercase and all slashes into backslashes."""
85-
if not isinstance(s, (bytes, str)):
86-
raise TypeError("normcase() argument must be str or bytes, "
87-
"not '{}'".format(s.__class__.__name__))
88-
return s.replace(_get_altsep(s), _get_sep(s)).lower()
49+
try:
50+
if isinstance(s, bytes):
51+
return s.replace(b'/', b'\\').lower()
52+
else:
53+
return s.replace('/', '\\').lower()
54+
except (TypeError, AttributeError):
55+
if not isinstance(s, (bytes, str)):
56+
raise TypeError("normcase() argument must be str or bytes, "
57+
"not %r" % s.__class__.__name__) from None
58+
raise
8959

9060

9161
# Return whether a path is absolute.
@@ -97,14 +67,19 @@ def normcase(s):
9767
def isabs(s):
9868
"""Test whether a path is absolute"""
9969
s = splitdrive(s)[1]
100-
return len(s) > 0 and s[:1] in _get_bothseps(s)
70+
return len(s) > 0 and s[0] in _get_bothseps(s)
10171

10272

10373
# Join two (or more) paths.
10474
def join(path, *paths):
105-
sep = _get_sep(path)
106-
seps = _get_bothseps(path)
107-
colon = _get_colon(path)
75+
if isinstance(path, bytes):
76+
sep = b'\\'
77+
seps = b'\\/'
78+
colon = b':'
79+
else:
80+
sep = '\\'
81+
seps = '\\/'
82+
colon = ':'
10883
result_drive, result_path = splitdrive(path)
10984
for p in paths:
11085
p_drive, p_path = splitdrive(p)
@@ -155,29 +130,35 @@ def splitdrive(p):
155130
Paths cannot contain both a drive letter and a UNC path.
156131
157132
"""
158-
empty = _get_empty(p)
159-
if len(p) > 1:
160-
sep = _get_sep(p)
161-
normp = p.replace(_get_altsep(p), sep)
133+
if len(p) >= 2:
134+
if isinstance(p, bytes):
135+
sep = b'\\'
136+
altsep = b'/'
137+
colon = b':'
138+
else:
139+
sep = '\\'
140+
altsep = '/'
141+
colon = ':'
142+
normp = p.replace(altsep, sep)
162143
if (normp[0:2] == sep*2) and (normp[2:3] != sep):
163144
# is a UNC path:
164145
# vvvvvvvvvvvvvvvvvvvv drive letter or UNC path
165146
# \\machine\mountpoint\directory\etc\...
166147
# directory ^^^^^^^^^^^^^^^
167148
index = normp.find(sep, 2)
168149
if index == -1:
169-
return empty, p
150+
return p[:0], p
170151
index2 = normp.find(sep, index + 1)
171152
# a UNC path can't have two slashes in a row
172153
# (after the initial two)
173154
if index2 == index + 1:
174-
return empty, p
155+
return p[:0], p
175156
if index2 == -1:
176157
index2 = len(p)
177158
return p[:index2], p[index2:]
178-
if normp[1:2] == _get_colon(p):
159+
if normp[1:2] == colon:
179160
return p[:2], p[2:]
180-
return empty, p
161+
return p[:0], p
181162

182163

183164
# Parse UNC paths
@@ -221,10 +202,7 @@ def split(p):
221202
i -= 1
222203
head, tail = p[:i], p[i:] # now tail has no slashes
223204
# remove trailing slashes from head, unless it's all slashes
224-
head2 = head
225-
while head2 and head2[-1:] in seps:
226-
head2 = head2[:-1]
227-
head = head2 or head
205+
head = head.rstrip(seps) or head
228206
return d + head, tail
229207

230208

@@ -234,8 +212,10 @@ def split(p):
234212
# It is always true that root + ext == p.
235213

236214
def splitext(p):
237-
return genericpath._splitext(p, _get_sep(p), _get_altsep(p),
238-
_get_dot(p))
215+
if isinstance(p, bytes):
216+
return genericpath._splitext(p, b'\\', b'/', b'.')
217+
else:
218+
return genericpath._splitext(p, '\\', '/', '.')
239219
splitext.__doc__ = genericpath._splitext.__doc__
240220

241221

@@ -343,7 +323,7 @@ def expanduser(path):
343323
userhome = join(drive, os.environ['HOMEPATH'])
344324

345325
if isinstance(path, bytes):
346-
userhome = userhome.encode(sys.getfilesystemencoding())
326+
userhome = os.fsencode(userhome)
347327

348328
if i != 1: #~user
349329
userhome = join(dirname(userhome), path[1:i])
@@ -369,13 +349,14 @@ def expandvars(path):
369349
370350
Unknown variables are left unchanged."""
371351
if isinstance(path, bytes):
372-
if ord('$') not in path and ord('%') not in path:
352+
if b'$' not in path and b'%' not in path:
373353
return path
374354
import string
375355
varchars = bytes(string.ascii_letters + string.digits + '_-', 'ascii')
376356
quote = b'\''
377357
percent = b'%'
378358
brace = b'{'
359+
rbrace = b'}'
379360
dollar = b'$'
380361
environ = getattr(os, 'environb', None)
381362
else:
@@ -386,6 +367,7 @@ def expandvars(path):
386367
quote = '\''
387368
percent = '%'
388369
brace = '{'
370+
rbrace = '}'
389371
dollar = '$'
390372
environ = os.environ
391373
res = path[:0]
@@ -432,15 +414,9 @@ def expandvars(path):
432414
path = path[index+2:]
433415
pathlen = len(path)
434416
try:
435-
if isinstance(path, bytes):
436-
index = path.index(b'}')
437-
else:
438-
index = path.index('}')
417+
index = path.index(rbrace)
439418
except ValueError:
440-
if isinstance(path, bytes):
441-
res += b'${' + path
442-
else:
443-
res += '${' + path
419+
res += dollar + brace + path
444420
index = pathlen - 1
445421
else:
446422
var = path[:index]
@@ -450,10 +426,7 @@ def expandvars(path):
450426
else:
451427
value = environ[var]
452428
except KeyError:
453-
if isinstance(path, bytes):
454-
value = b'${' + var + b'}'
455-
else:
456-
value = '${' + var + '}'
429+
value = dollar + brace + var + rbrace
457430
res += value
458431
else:
459432
var = path[:0]
@@ -485,16 +458,25 @@ def expandvars(path):
485458

486459
def normpath(path):
487460
"""Normalize path, eliminating double slashes, etc."""
488-
sep = _get_sep(path)
489-
dotdot = _get_dot(path) * 2
490-
special_prefixes = _get_special(path)
461+
if isinstance(path, bytes):
462+
sep = b'\\'
463+
altsep = b'/'
464+
curdir = b'.'
465+
pardir = b'..'
466+
special_prefixes = (b'\\\\.\\', b'\\\\?\\')
467+
else:
468+
sep = '\\'
469+
altsep = '/'
470+
curdir = '.'
471+
pardir = '..'
472+
special_prefixes = ('\\\\.\\', '\\\\?\\')
491473
if path.startswith(special_prefixes):
492474
# in the case of paths with these prefixes:
493475
# \\.\ -> device names
494476
# \\?\ -> literal paths
495477
# do not do any normalization, but return the path unchanged
496478
return path
497-
path = path.replace(_get_altsep(path), sep)
479+
path = path.replace(altsep, sep)
498480
prefix, path = splitdrive(path)
499481

500482
# collapse initial backslashes
@@ -505,21 +487,21 @@ def normpath(path):
505487
comps = path.split(sep)
506488
i = 0
507489
while i < len(comps):
508-
if not comps[i] or comps[i] == _get_dot(path):
490+
if not comps[i] or comps[i] == curdir:
509491
del comps[i]
510-
elif comps[i] == dotdot:
511-
if i > 0 and comps[i-1] != dotdot:
492+
elif comps[i] == pardir:
493+
if i > 0 and comps[i-1] != pardir:
512494
del comps[i-1:i+1]
513495
i -= 1
514-
elif i == 0 and prefix.endswith(_get_sep(path)):
496+
elif i == 0 and prefix.endswith(sep):
515497
del comps[i]
516498
else:
517499
i += 1
518500
else:
519501
i += 1
520502
# If the path is now empty, substitute '.'
521503
if not prefix and not comps:
522-
comps.append(_get_dot(path))
504+
comps.append(curdir)
523505
return prefix + sep.join(comps)
524506

525507

@@ -559,12 +541,19 @@ def abspath(path):
559541
supports_unicode_filenames = (hasattr(sys, "getwindowsversion") and
560542
sys.getwindowsversion()[3] >= 2)
561543

562-
def relpath(path, start=curdir):
544+
def relpath(path, start=None):
563545
"""Return a relative version of a path"""
564-
sep = _get_sep(path)
546+
if isinstance(path, bytes):
547+
sep = b'\\'
548+
curdir = b'.'
549+
pardir = b'..'
550+
else:
551+
sep = '\\'
552+
curdir = '.'
553+
pardir = '..'
565554

566-
if start is curdir:
567-
start = _get_dot(path)
555+
if start is None:
556+
start = curdir
568557

569558
if not path:
570559
raise ValueError("no path specified")
@@ -574,9 +563,8 @@ def relpath(path, start=curdir):
574563
start_drive, start_rest = splitdrive(start_abs)
575564
path_drive, path_rest = splitdrive(path_abs)
576565
if normcase(start_drive) != normcase(path_drive):
577-
error = "path is on mount '{0}', start on mount '{1}'".format(
578-
path_drive, start_drive)
579-
raise ValueError(error)
566+
raise ValueError("path is on mount %r, start on mount %r" % (
567+
path_drive, start_drive))
580568

581569
start_list = [x for x in start_rest.split(sep) if x]
582570
path_list = [x for x in path_rest.split(sep) if x]
@@ -587,13 +575,9 @@ def relpath(path, start=curdir):
587575
break
588576
i += 1
589577

590-
if isinstance(path, bytes):
591-
pardir = b'..'
592-
else:
593-
pardir = '..'
594578
rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
595579
if not rel_list:
596-
return _get_dot(path)
580+
return curdir
597581
return join(*rel_list)
598582

599583

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ Core and Builtins
108108
Library
109109
-------
110110

111+
- Issue #15275: Clean up and speed up the ntpath module.
112+
111113
- Issue #21888: plistlib's load() and loads() now work if the fmt parameter is
112114
specified.
113115

0 commit comments

Comments
 (0)
0