mirror of
https://github.com/python/cpython.git
synced 2025-08-25 03:04:55 +00:00
Issue #6815: os.path.expandvars() now supports non-ASCII environment
variables names and values.
This commit is contained in:
commit
7dfaa27fdd
5 changed files with 96 additions and 42 deletions
|
@ -377,6 +377,7 @@ def expandvars(path):
|
||||||
percent = b'%'
|
percent = b'%'
|
||||||
brace = b'{'
|
brace = b'{'
|
||||||
dollar = b'$'
|
dollar = b'$'
|
||||||
|
environ = getattr(os, 'environb', None)
|
||||||
else:
|
else:
|
||||||
if '$' not in path and '%' not in path:
|
if '$' not in path and '%' not in path:
|
||||||
return path
|
return path
|
||||||
|
@ -386,6 +387,7 @@ def expandvars(path):
|
||||||
percent = '%'
|
percent = '%'
|
||||||
brace = '{'
|
brace = '{'
|
||||||
dollar = '$'
|
dollar = '$'
|
||||||
|
environ = os.environ
|
||||||
res = path[:0]
|
res = path[:0]
|
||||||
index = 0
|
index = 0
|
||||||
pathlen = len(path)
|
pathlen = len(path)
|
||||||
|
@ -414,14 +416,13 @@ def expandvars(path):
|
||||||
index = pathlen - 1
|
index = pathlen - 1
|
||||||
else:
|
else:
|
||||||
var = path[:index]
|
var = path[:index]
|
||||||
if isinstance(path, bytes):
|
try:
|
||||||
var = var.decode('ascii')
|
if environ is None:
|
||||||
if var in os.environ:
|
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||||
value = os.environ[var]
|
else:
|
||||||
else:
|
value = environ[var]
|
||||||
value = '%' + var + '%'
|
except KeyError:
|
||||||
if isinstance(path, bytes):
|
value = percent + var + percent
|
||||||
value = value.encode('ascii')
|
|
||||||
res += value
|
res += value
|
||||||
elif c == dollar: # variable or '$$'
|
elif c == dollar: # variable or '$$'
|
||||||
if path[index + 1:index + 2] == dollar:
|
if path[index + 1:index + 2] == dollar:
|
||||||
|
@ -435,39 +436,40 @@ def expandvars(path):
|
||||||
index = path.index(b'}')
|
index = path.index(b'}')
|
||||||
else:
|
else:
|
||||||
index = path.index('}')
|
index = path.index('}')
|
||||||
var = path[:index]
|
|
||||||
if isinstance(path, bytes):
|
|
||||||
var = var.decode('ascii')
|
|
||||||
if var in os.environ:
|
|
||||||
value = os.environ[var]
|
|
||||||
else:
|
|
||||||
value = '${' + var + '}'
|
|
||||||
if isinstance(path, bytes):
|
|
||||||
value = value.encode('ascii')
|
|
||||||
res += value
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
if isinstance(path, bytes):
|
if isinstance(path, bytes):
|
||||||
res += b'${' + path
|
res += b'${' + path
|
||||||
else:
|
else:
|
||||||
res += '${' + path
|
res += '${' + path
|
||||||
index = pathlen - 1
|
index = pathlen - 1
|
||||||
|
else:
|
||||||
|
var = path[:index]
|
||||||
|
try:
|
||||||
|
if environ is None:
|
||||||
|
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||||
|
else:
|
||||||
|
value = environ[var]
|
||||||
|
except KeyError:
|
||||||
|
if isinstance(path, bytes):
|
||||||
|
value = b'${' + var + b'}'
|
||||||
|
else:
|
||||||
|
value = '${' + var + '}'
|
||||||
|
res += value
|
||||||
else:
|
else:
|
||||||
var = ''
|
var = path[:0]
|
||||||
index += 1
|
index += 1
|
||||||
c = path[index:index + 1]
|
c = path[index:index + 1]
|
||||||
while c and c in varchars:
|
while c and c in varchars:
|
||||||
if isinstance(path, bytes):
|
var += c
|
||||||
var += c.decode('ascii')
|
|
||||||
else:
|
|
||||||
var += c
|
|
||||||
index += 1
|
index += 1
|
||||||
c = path[index:index + 1]
|
c = path[index:index + 1]
|
||||||
if var in os.environ:
|
try:
|
||||||
value = os.environ[var]
|
if environ is None:
|
||||||
else:
|
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||||
value = '$' + var
|
else:
|
||||||
if isinstance(path, bytes):
|
value = environ[var]
|
||||||
value = value.encode('ascii')
|
except KeyError:
|
||||||
|
value = dollar + var
|
||||||
res += value
|
res += value
|
||||||
if c:
|
if c:
|
||||||
index -= 1
|
index -= 1
|
||||||
|
|
|
@ -279,6 +279,7 @@ def expandvars(path):
|
||||||
search = _varprogb.search
|
search = _varprogb.search
|
||||||
start = b'{'
|
start = b'{'
|
||||||
end = b'}'
|
end = b'}'
|
||||||
|
environ = getattr(os, 'environb', None)
|
||||||
else:
|
else:
|
||||||
if '$' not in path:
|
if '$' not in path:
|
||||||
return path
|
return path
|
||||||
|
@ -288,6 +289,7 @@ def expandvars(path):
|
||||||
search = _varprog.search
|
search = _varprog.search
|
||||||
start = '{'
|
start = '{'
|
||||||
end = '}'
|
end = '}'
|
||||||
|
environ = os.environ
|
||||||
i = 0
|
i = 0
|
||||||
while True:
|
while True:
|
||||||
m = search(path, i)
|
m = search(path, i)
|
||||||
|
@ -297,18 +299,18 @@ def expandvars(path):
|
||||||
name = m.group(1)
|
name = m.group(1)
|
||||||
if name.startswith(start) and name.endswith(end):
|
if name.startswith(start) and name.endswith(end):
|
||||||
name = name[1:-1]
|
name = name[1:-1]
|
||||||
if isinstance(name, bytes):
|
try:
|
||||||
name = str(name, 'ASCII')
|
if environ is None:
|
||||||
if name in os.environ:
|
value = os.fsencode(os.environ[os.fsdecode(var)])
|
||||||
|
else:
|
||||||
|
value = environ[name]
|
||||||
|
except KeyError:
|
||||||
|
i = j
|
||||||
|
else:
|
||||||
tail = path[j:]
|
tail = path[j:]
|
||||||
value = os.environ[name]
|
|
||||||
if isinstance(path, bytes):
|
|
||||||
value = value.encode('ASCII')
|
|
||||||
path = path[:i] + value
|
path = path[:i] + value
|
||||||
i = len(path)
|
i = len(path)
|
||||||
path += tail
|
path += tail
|
||||||
else:
|
|
||||||
i = j
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -329,7 +329,6 @@ class CommonTest(GenericTest):
|
||||||
self.assertEqual(expandvars("$[foo]bar"), "$[foo]bar")
|
self.assertEqual(expandvars("$[foo]bar"), "$[foo]bar")
|
||||||
self.assertEqual(expandvars("$bar bar"), "$bar bar")
|
self.assertEqual(expandvars("$bar bar"), "$bar bar")
|
||||||
self.assertEqual(expandvars("$?bar"), "$?bar")
|
self.assertEqual(expandvars("$?bar"), "$?bar")
|
||||||
self.assertEqual(expandvars("${foo}bar"), "barbar")
|
|
||||||
self.assertEqual(expandvars("$foo}bar"), "bar}bar")
|
self.assertEqual(expandvars("$foo}bar"), "bar}bar")
|
||||||
self.assertEqual(expandvars("${foo"), "${foo")
|
self.assertEqual(expandvars("${foo"), "${foo")
|
||||||
self.assertEqual(expandvars("${{foo}}"), "baz1}")
|
self.assertEqual(expandvars("${{foo}}"), "baz1}")
|
||||||
|
@ -342,13 +341,40 @@ class CommonTest(GenericTest):
|
||||||
self.assertEqual(expandvars(b"$[foo]bar"), b"$[foo]bar")
|
self.assertEqual(expandvars(b"$[foo]bar"), b"$[foo]bar")
|
||||||
self.assertEqual(expandvars(b"$bar bar"), b"$bar bar")
|
self.assertEqual(expandvars(b"$bar bar"), b"$bar bar")
|
||||||
self.assertEqual(expandvars(b"$?bar"), b"$?bar")
|
self.assertEqual(expandvars(b"$?bar"), b"$?bar")
|
||||||
self.assertEqual(expandvars(b"${foo}bar"), b"barbar")
|
|
||||||
self.assertEqual(expandvars(b"$foo}bar"), b"bar}bar")
|
self.assertEqual(expandvars(b"$foo}bar"), b"bar}bar")
|
||||||
self.assertEqual(expandvars(b"${foo"), b"${foo")
|
self.assertEqual(expandvars(b"${foo"), b"${foo")
|
||||||
self.assertEqual(expandvars(b"${{foo}}"), b"baz1}")
|
self.assertEqual(expandvars(b"${{foo}}"), b"baz1}")
|
||||||
self.assertEqual(expandvars(b"$foo$foo"), b"barbar")
|
self.assertEqual(expandvars(b"$foo$foo"), b"barbar")
|
||||||
self.assertEqual(expandvars(b"$bar$bar"), b"$bar$bar")
|
self.assertEqual(expandvars(b"$bar$bar"), b"$bar$bar")
|
||||||
|
|
||||||
|
@unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII')
|
||||||
|
def test_expandvars_nonascii(self):
|
||||||
|
if self.pathmodule.__name__ == 'macpath':
|
||||||
|
self.skipTest('macpath.expandvars is a stub')
|
||||||
|
expandvars = self.pathmodule.expandvars
|
||||||
|
def check(value, expected):
|
||||||
|
self.assertEqual(expandvars(value), expected)
|
||||||
|
with support.EnvironmentVarGuard() as env:
|
||||||
|
env.clear()
|
||||||
|
nonascii = support.FS_NONASCII
|
||||||
|
env['spam'] = nonascii
|
||||||
|
env[nonascii] = 'ham' + nonascii
|
||||||
|
check(nonascii, nonascii)
|
||||||
|
check('$spam bar', '%s bar' % nonascii)
|
||||||
|
check('${spam}bar', '%sbar' % nonascii)
|
||||||
|
check('${%s}bar' % nonascii, 'ham%sbar' % nonascii)
|
||||||
|
check('$bar%s bar' % nonascii, '$bar%s bar' % nonascii)
|
||||||
|
check('$spam}bar', '%s}bar' % nonascii)
|
||||||
|
|
||||||
|
check(os.fsencode(nonascii), os.fsencode(nonascii))
|
||||||
|
check(b'$spam bar', os.fsencode('%s bar' % nonascii))
|
||||||
|
check(b'${spam}bar', os.fsencode('%sbar' % nonascii))
|
||||||
|
check(os.fsencode('${%s}bar' % nonascii),
|
||||||
|
os.fsencode('ham%sbar' % nonascii))
|
||||||
|
check(os.fsencode('$bar%s bar' % nonascii),
|
||||||
|
os.fsencode('$bar%s bar' % nonascii))
|
||||||
|
check(b'$spam}bar', os.fsencode('%s}bar' % nonascii))
|
||||||
|
|
||||||
def test_abspath(self):
|
def test_abspath(self):
|
||||||
self.assertIn("foo", self.pathmodule.abspath("foo"))
|
self.assertIn("foo", self.pathmodule.abspath("foo"))
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
|
|
|
@ -22,13 +22,15 @@ def tester(fn, wantResult):
|
||||||
fn = fn.replace('["', '[b"')
|
fn = fn.replace('["', '[b"')
|
||||||
fn = fn.replace(", '", ", b'")
|
fn = fn.replace(", '", ", b'")
|
||||||
fn = fn.replace(', "', ', b"')
|
fn = fn.replace(', "', ', b"')
|
||||||
|
fn = os.fsencode(fn).decode('latin1')
|
||||||
|
fn = fn.encode('ascii', 'backslashreplace').decode('ascii')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter("ignore", DeprecationWarning)
|
warnings.simplefilter("ignore", DeprecationWarning)
|
||||||
gotResult = eval(fn)
|
gotResult = eval(fn)
|
||||||
if isinstance(wantResult, str):
|
if isinstance(wantResult, str):
|
||||||
wantResult = wantResult.encode('ascii')
|
wantResult = os.fsencode(wantResult)
|
||||||
elif isinstance(wantResult, tuple):
|
elif isinstance(wantResult, tuple):
|
||||||
wantResult = tuple(r.encode('ascii') for r in wantResult)
|
wantResult = tuple(os.fsencode(r) for r in wantResult)
|
||||||
|
|
||||||
gotResult = eval(fn)
|
gotResult = eval(fn)
|
||||||
if wantResult != gotResult:
|
if wantResult != gotResult:
|
||||||
|
@ -223,7 +225,6 @@ class TestNtpath(unittest.TestCase):
|
||||||
tester('ntpath.expandvars("$[foo]bar")', "$[foo]bar")
|
tester('ntpath.expandvars("$[foo]bar")', "$[foo]bar")
|
||||||
tester('ntpath.expandvars("$bar bar")', "$bar bar")
|
tester('ntpath.expandvars("$bar bar")', "$bar bar")
|
||||||
tester('ntpath.expandvars("$?bar")', "$?bar")
|
tester('ntpath.expandvars("$?bar")', "$?bar")
|
||||||
tester('ntpath.expandvars("${foo}bar")', "barbar")
|
|
||||||
tester('ntpath.expandvars("$foo}bar")', "bar}bar")
|
tester('ntpath.expandvars("$foo}bar")', "bar}bar")
|
||||||
tester('ntpath.expandvars("${foo")', "${foo")
|
tester('ntpath.expandvars("${foo")', "${foo")
|
||||||
tester('ntpath.expandvars("${{foo}}")', "baz1}")
|
tester('ntpath.expandvars("${{foo}}")', "baz1}")
|
||||||
|
@ -237,6 +238,26 @@ class TestNtpath(unittest.TestCase):
|
||||||
tester('ntpath.expandvars("%foo%%bar")', "bar%bar")
|
tester('ntpath.expandvars("%foo%%bar")', "bar%bar")
|
||||||
tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
|
tester('ntpath.expandvars("\'%foo%\'%bar")', "\'%foo%\'%bar")
|
||||||
|
|
||||||
|
@unittest.skipUnless(support.FS_NONASCII, 'need support.FS_NONASCII')
|
||||||
|
def test_expandvars_nonascii(self):
|
||||||
|
def check(value, expected):
|
||||||
|
tester('ntpath.expandvars(%r)' % value, expected)
|
||||||
|
with support.EnvironmentVarGuard() as env:
|
||||||
|
env.clear()
|
||||||
|
nonascii = support.FS_NONASCII
|
||||||
|
env['spam'] = nonascii
|
||||||
|
env[nonascii] = 'ham' + nonascii
|
||||||
|
check('$spam bar', '%s bar' % nonascii)
|
||||||
|
check('$%s bar' % nonascii, '$%s bar' % nonascii)
|
||||||
|
check('${spam}bar', '%sbar' % nonascii)
|
||||||
|
check('${%s}bar' % nonascii, 'ham%sbar' % nonascii)
|
||||||
|
check('$spam}bar', '%s}bar' % nonascii)
|
||||||
|
check('$%s}bar' % nonascii, '$%s}bar' % nonascii)
|
||||||
|
check('%spam% bar', '%s bar' % nonascii)
|
||||||
|
check('%{}% bar'.format(nonascii), 'ham%s bar' % nonascii)
|
||||||
|
check('%spam%bar', '%sbar' % nonascii)
|
||||||
|
check('%{}%bar'.format(nonascii), 'ham%sbar' % nonascii)
|
||||||
|
|
||||||
def test_abspath(self):
|
def test_abspath(self):
|
||||||
# ntpath.abspath() can only be used on a system with the "nt" module
|
# ntpath.abspath() can only be used on a system with the "nt" module
|
||||||
# (reasonably), so we protect this test with "import nt". This allows
|
# (reasonably), so we protect this test with "import nt". This allows
|
||||||
|
|
|
@ -15,6 +15,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #6815: os.path.expandvars() now supports non-ASCII environment
|
||||||
|
variables names and values.
|
||||||
|
|
||||||
- Issue #17671: Fixed a crash when use non-initialized io.BufferedRWPair.
|
- Issue #17671: Fixed a crash when use non-initialized io.BufferedRWPair.
|
||||||
Based on patch by Stephen Tu.
|
Based on patch by Stephen Tu.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue