Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Add nt._path_splitroot and update ntpath #4077

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

104 changes: 86 additions & 18 deletions Lib/fnmatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,19 @@
The function translate(PATTERN) returns a regular expression
corresponding to PATTERN. (It does not compile it.)
"""
try:
import os
except ImportError:
import _dummy_os as os
import os
import posixpath
import re
import functools

__all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]

# Build a thread-safe incrementing counter to help create unique regexp group
# names across calls.
from itertools import count
_nextgroupnum = count().__next__
del count

def fnmatch(name, pat):
"""Test whether FILENAME matches PATTERN.

Expand Down Expand Up @@ -49,7 +52,7 @@ def _compile_pattern(pat):
return re.compile(res).match

def filter(names, pat):
"""Return the subset of the list NAMES that match PAT."""
"""Construct a list from those elements of the iterable NAMES that match PAT."""
result = []
pat = os.path.normcase(pat)
match = _compile_pattern(pat)
Expand Down Expand Up @@ -80,15 +83,19 @@ def translate(pat):
There is no way to quote meta-characters.
"""

STAR = object()
res = []
add = res.append
i, n = 0, len(pat)
res = ''
while i < n:
c = pat[i]
i = i+1
if c == '*':
res = res + '.*'
# compress consecutive `*` into one
if (not res) or res[-1] is not STAR:
add(STAR)
elif c == '?':
res = res + '.'
add('.')
elif c == '[':
j = i
if j < n and pat[j] == '!':
Expand All @@ -98,10 +105,10 @@ def translate(pat):
while j < n and pat[j] != ']':
j = j+1
if j >= n:
res = res + '\\['
add('\\[')
else:
stuff = pat[i:j]
if '--' not in stuff:
if '-' not in stuff:
stuff = stuff.replace('\\', r'\\')
else:
chunks = []
Expand All @@ -113,19 +120,80 @@ def translate(pat):
chunks.append(pat[i:k])
i = k+1
k = k+3
chunks.append(pat[i:j])
chunk = pat[i:j]
if chunk:
chunks.append(chunk)
else:
chunks[-1] += '-'
# Remove empty ranges -- invalid in RE.
for k in range(len(chunks)-1, 0, -1):
if chunks[k-1][-1] > chunks[k][0]:
chunks[k-1] = chunks[k-1][:-1] + chunks[k][1:]
del chunks[k]
# Escape backslashes and hyphens for set difference (--).
# Hyphens that create ranges shouldn't be escaped.
stuff = '-'.join(s.replace('\\', r'\\').replace('-', r'\-')
for s in chunks)
# Escape set operations (&&, ~~ and ||).
stuff = re.sub(r'([&~|])', r'\\\1', stuff)
i = j+1
if stuff[0] == '!':
stuff = '^' + stuff[1:]
elif stuff[0] in ('^', '['):
stuff = '\\' + stuff
res = '%s[%s]' % (res, stuff)
if not stuff:
# Empty range: never match.
add('(?!)')
elif stuff == '!':
# Negated empty range: match any character.
add('.')
else:
if stuff[0] == '!':
stuff = '^' + stuff[1:]
elif stuff[0] in ('^', '['):
stuff = '\\' + stuff
add(f'[{stuff}]')
else:
add(re.escape(c))
assert i == n

# Deal with STARs.
inp = res
res = []
add = res.append
i, n = 0, len(inp)
# Fixed pieces at the start?
while i < n and inp[i] is not STAR:
add(inp[i])
i += 1
# Now deal with STAR fixed STAR fixed ...
# For an interior `STAR fixed` pairing, we want to do a minimal
# .*? match followed by `fixed`, with no possibility of backtracking.
# We can't spell that directly, but can trick it into working by matching
# .*?fixed
# in a lookahead assertion, save the matched part in a group, then
# consume that group via a backreference. If the overall match fails,
# the lookahead assertion won't try alternatives. So the translation is:
# (?=(?P<name>.*?fixed))(?P=name)
# Group names are created as needed: g0, g1, g2, ...
# The numbers are obtained from _nextgroupnum() to ensure they're unique
# across calls and across threads. This is because people rely on the
# undocumented ability to join multiple translate() results together via
# "|" to build large regexps matching "one of many" shell patterns.
while i < n:
assert inp[i] is STAR
i += 1
if i == n:
add(".*")
break
assert inp[i] is not STAR
fixed = []
while i < n and inp[i] is not STAR:
fixed.append(inp[i])
i += 1
fixed = "".join(fixed)
if i == n:
add(".*")
add(fixed)
else:
res = res + re.escape(c)
return r'(?s:%s)\Z' % res
groupnum = _nextgroupnum()
add(f"(?=(?P<g{groupnum}>.*?{fixed}))(?P=g{groupnum})")
assert i == n
res = "".join(res)
return fr'(?s:{res})\Z'
65 changes: 53 additions & 12 deletions Lib/ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import genericpath
from genericpath import *


__all__ = ["normcase","isabs","join","splitdrive","split","splitext",
"basename","dirname","commonprefix","getsize","getmtime",
"getatime","getctime", "islink","exists","lexists","isdir","isfile",
Expand All @@ -41,14 +42,39 @@ def _get_bothseps(path):
# Other normalizations (such as optimizing '../' away) are not done
# (this is done by normpath).

def normcase(s):
"""Normalize case of pathname.

Makes all characters lowercase and all slashes into backslashes."""
s = os.fspath(s)
if isinstance(s, bytes):
return s.replace(b'/', b'\\').lower()
else:
try:
from _winapi import (
LCMapStringEx as _LCMapStringEx,
LOCALE_NAME_INVARIANT as _LOCALE_NAME_INVARIANT,
LCMAP_LOWERCASE as _LCMAP_LOWERCASE)

def normcase(s):
"""Normalize case of pathname.

Makes all characters lowercase and all slashes into backslashes.
"""
s = os.fspath(s)
if not s:
return s
if isinstance(s, bytes):
encoding = sys.getfilesystemencoding()
s = s.decode(encoding, 'surrogateescape').replace('/', '\\')
s = _LCMapStringEx(_LOCALE_NAME_INVARIANT,
_LCMAP_LOWERCASE, s)
return s.encode(encoding, 'surrogateescape')
else:
return _LCMapStringEx(_LOCALE_NAME_INVARIANT,
_LCMAP_LOWERCASE,
s.replace('/', '\\'))
except ImportError:
def normcase(s):
"""Normalize case of pathname.

Makes all characters lowercase and all slashes into backslashes.
"""
s = os.fspath(s)
if isinstance(s, bytes):
return os.fsencode(os.fsdecode(s).replace('/', '\\').lower())
return s.replace('/', '\\').lower()


Expand Down Expand Up @@ -312,12 +338,25 @@ def expanduser(path):
drive = ''
userhome = join(drive, os.environ['HOMEPATH'])

if i != 1: #~user
target_user = path[1:i]
if isinstance(target_user, bytes):
target_user = os.fsdecode(target_user)
current_user = os.environ.get('USERNAME')

if target_user != current_user:
# Try to guess user home directory. By default all user
# profile directories are located in the same place and are
# named by corresponding usernames. If userhome isn't a
# normal profile directory, this guess is likely wrong,
# so we bail out.
if current_user != basename(userhome):
return path
userhome = join(dirname(userhome), target_user)

if isinstance(path, bytes):
userhome = os.fsencode(userhome)

if i != 1: #~user
userhome = join(dirname(userhome), path[1:i])

return userhome + path[i:]


Expand Down Expand Up @@ -622,7 +661,7 @@ def _getfinalpathname_nonstrict(path):
tail = join(name, tail) if tail else name
return tail

def realpath(path):
def realpath(path, *, strict=False):
path = normpath(path)
if isinstance(path, bytes):
prefix = b'\\\\?\\'
Expand All @@ -647,6 +686,8 @@ def realpath(path):
path = _getfinalpathname(path)
initial_winerror = 0
except OSError as ex:
if strict:
raise
initial_winerror = ex.winerror
path = _getfinalpathname_nonstrict(path)
# The path returned by _getfinalpathname will always start with \\?\ -
Expand Down
Loading