mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +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'%' | ||||
|         brace = b'{' | ||||
|         dollar = b'$' | ||||
|         environ = getattr(os, 'environb', None) | ||||
|     else: | ||||
|         if '$' not in path and '%' not in path: | ||||
|             return path | ||||
|  | @ -386,6 +387,7 @@ def expandvars(path): | |||
|         percent = '%' | ||||
|         brace = '{' | ||||
|         dollar = '$' | ||||
|         environ = os.environ | ||||
|     res = path[:0] | ||||
|     index = 0 | ||||
|     pathlen = len(path) | ||||
|  | @ -414,14 +416,13 @@ def expandvars(path): | |||
|                     index = pathlen - 1 | ||||
|                 else: | ||||
|                     var = path[:index] | ||||
|                     if isinstance(path, bytes): | ||||
|                         var = var.decode('ascii') | ||||
|                     if var in os.environ: | ||||
|                         value = os.environ[var] | ||||
|                     try: | ||||
|                         if environ is None: | ||||
|                             value = os.fsencode(os.environ[os.fsdecode(var)]) | ||||
|                         else: | ||||
|                         value = '%' + var + '%' | ||||
|                     if isinstance(path, bytes): | ||||
|                         value = value.encode('ascii') | ||||
|                             value = environ[var] | ||||
|                     except KeyError: | ||||
|                         value = percent + var + percent | ||||
|                     res += value | ||||
|         elif c == dollar:  # variable or '$$' | ||||
|             if path[index + 1:index + 2] == dollar: | ||||
|  | @ -435,16 +436,6 @@ def expandvars(path): | |||
|                         index = path.index(b'}') | ||||
|                     else: | ||||
|                         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: | ||||
|                     if isinstance(path, bytes): | ||||
|                         res += b'${' + path | ||||
|  | @ -452,22 +443,33 @@ def expandvars(path): | |||
|                         res += '${' + path | ||||
|                     index = pathlen - 1 | ||||
|                 else: | ||||
|                 var = '' | ||||
|                     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: | ||||
|                 var = path[:0] | ||||
|                 index += 1 | ||||
|                 c = path[index:index + 1] | ||||
|                 while c and c in varchars: | ||||
|                     if isinstance(path, bytes): | ||||
|                         var += c.decode('ascii') | ||||
|                     else: | ||||
|                     var += c | ||||
|                     index += 1 | ||||
|                     c = path[index:index + 1] | ||||
|                 if var in os.environ: | ||||
|                     value = os.environ[var] | ||||
|                 try: | ||||
|                     if environ is None: | ||||
|                         value = os.fsencode(os.environ[os.fsdecode(var)]) | ||||
|                     else: | ||||
|                     value = '$' + var | ||||
|                 if isinstance(path, bytes): | ||||
|                     value = value.encode('ascii') | ||||
|                         value = environ[var] | ||||
|                 except KeyError: | ||||
|                     value = dollar + var | ||||
|                 res += value | ||||
|                 if c: | ||||
|                     index -= 1 | ||||
|  |  | |||
|  | @ -279,6 +279,7 @@ def expandvars(path): | |||
|         search = _varprogb.search | ||||
|         start = b'{' | ||||
|         end = b'}' | ||||
|         environ = getattr(os, 'environb', None) | ||||
|     else: | ||||
|         if '$' not in path: | ||||
|             return path | ||||
|  | @ -288,6 +289,7 @@ def expandvars(path): | |||
|         search = _varprog.search | ||||
|         start = '{' | ||||
|         end = '}' | ||||
|         environ = os.environ | ||||
|     i = 0 | ||||
|     while True: | ||||
|         m = search(path, i) | ||||
|  | @ -297,18 +299,18 @@ def expandvars(path): | |||
|         name = m.group(1) | ||||
|         if name.startswith(start) and name.endswith(end): | ||||
|             name = name[1:-1] | ||||
|         if isinstance(name, bytes): | ||||
|             name = str(name, 'ASCII') | ||||
|         if name in os.environ: | ||||
|         try: | ||||
|             if environ is None: | ||||
|                 value = os.fsencode(os.environ[os.fsdecode(var)]) | ||||
|             else: | ||||
|                 value = environ[name] | ||||
|         except KeyError: | ||||
|             i = j | ||||
|         else: | ||||
|             tail = path[j:] | ||||
|             value = os.environ[name] | ||||
|             if isinstance(path, bytes): | ||||
|                 value = value.encode('ASCII') | ||||
|             path = path[:i] + value | ||||
|             i = len(path) | ||||
|             path += tail | ||||
|         else: | ||||
|             i = j | ||||
|     return path | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -329,7 +329,6 @@ def test_expandvars(self): | |||
|             self.assertEqual(expandvars("$[foo]bar"), "$[foo]bar") | ||||
|             self.assertEqual(expandvars("$bar bar"), "$bar bar") | ||||
|             self.assertEqual(expandvars("$?bar"), "$?bar") | ||||
|             self.assertEqual(expandvars("${foo}bar"), "barbar") | ||||
|             self.assertEqual(expandvars("$foo}bar"), "bar}bar") | ||||
|             self.assertEqual(expandvars("${foo"), "${foo") | ||||
|             self.assertEqual(expandvars("${{foo}}"), "baz1}") | ||||
|  | @ -342,13 +341,40 @@ def test_expandvars(self): | |||
|             self.assertEqual(expandvars(b"$[foo]bar"), b"$[foo]bar") | ||||
|             self.assertEqual(expandvars(b"$bar bar"), b"$bar 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"), b"${foo") | ||||
|             self.assertEqual(expandvars(b"${{foo}}"), b"baz1}") | ||||
|             self.assertEqual(expandvars(b"$foo$foo"), b"barbar") | ||||
|             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): | ||||
|         self.assertIn("foo", self.pathmodule.abspath("foo")) | ||||
|         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 = os.fsencode(fn).decode('latin1') | ||||
|     fn = fn.encode('ascii', 'backslashreplace').decode('ascii') | ||||
|     with warnings.catch_warnings(): | ||||
|         warnings.simplefilter("ignore", DeprecationWarning) | ||||
|         gotResult = eval(fn) | ||||
|     if isinstance(wantResult, str): | ||||
|         wantResult = wantResult.encode('ascii') | ||||
|         wantResult = os.fsencode(wantResult) | ||||
|     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) | ||||
|     if wantResult != gotResult: | ||||
|  | @ -223,7 +225,6 @@ def test_expandvars(self): | |||
|             tester('ntpath.expandvars("$[foo]bar")', "$[foo]bar") | ||||
|             tester('ntpath.expandvars("$bar bar")', "$bar bar") | ||||
|             tester('ntpath.expandvars("$?bar")', "$?bar") | ||||
|             tester('ntpath.expandvars("${foo}bar")', "barbar") | ||||
|             tester('ntpath.expandvars("$foo}bar")', "bar}bar") | ||||
|             tester('ntpath.expandvars("${foo")', "${foo") | ||||
|             tester('ntpath.expandvars("${{foo}}")', "baz1}") | ||||
|  | @ -237,6 +238,26 @@ def test_expandvars(self): | |||
|             tester('ntpath.expandvars("%foo%%bar")', "bar%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): | ||||
|         # ntpath.abspath() can only be used on a system with the "nt" module | ||||
|         # (reasonably), so we protect this test with "import nt".  This allows | ||||
|  |  | |||
|  | @ -15,6 +15,9 @@ Core and Builtins | |||
| 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. | ||||
|   Based on patch by Stephen Tu. | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Serhiy Storchaka
						Serhiy Storchaka