@@ -335,17 +335,23 @@ def expanduser(path):
335335# XXX With COMMAND.COM you can use any characters in a variable name,
336336# XXX except '^|<>='.
337337
338+ _varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"
339+ _varsub = None
340+ _varsubb = None
341+
338342def expandvars (path ):
339343 """Expand shell variables of the forms $var, ${var} and %var%.
340344
341345 Unknown variables are left unchanged."""
342346 path = os .fspath (path )
347+ global _varsub , _varsubb
343348 if isinstance (path , bytes ):
344349 if b'$' not in path and b'%' not in path :
345350 return path
346- import string
347- varchars = bytes (string .ascii_letters + string .digits + '_-' , 'ascii' )
348- quote = b'\' '
351+ if not _varsubb :
352+ import re
353+ _varsubb = re .compile (_varpattern .encode (), re .ASCII ).sub
354+ sub = _varsubb
349355 percent = b'%'
350356 brace = b'{'
351357 rbrace = b'}'
@@ -354,94 +360,44 @@ def expandvars(path):
354360 else :
355361 if '$' not in path and '%' not in path :
356362 return path
357- import string
358- varchars = string .ascii_letters + string .digits + '_-'
359- quote = '\' '
363+ if not _varsub :
364+ import re
365+ _varsub = re .compile (_varpattern , re .ASCII ).sub
366+ sub = _varsub
360367 percent = '%'
361368 brace = '{'
362369 rbrace = '}'
363370 dollar = '$'
364371 environ = os .environ
365- res = path [:0 ]
366- index = 0
367- pathlen = len (path )
368- while index < pathlen :
369- c = path [index :index + 1 ]
370- if c == quote : # no expansion within single quotes
371- path = path [index + 1 :]
372- pathlen = len (path )
373- try :
374- index = path .index (c )
375- res += c + path [:index + 1 ]
376- except ValueError :
377- res += c + path
378- index = pathlen - 1
379- elif c == percent : # variable or '%'
380- if path [index + 1 :index + 2 ] == percent :
381- res += c
382- index += 1
383- else :
384- path = path [index + 1 :]
385- pathlen = len (path )
386- try :
387- index = path .index (percent )
388- except ValueError :
389- res += percent + path
390- index = pathlen - 1
391- else :
392- var = path [:index ]
393- try :
394- if environ is None :
395- value = os .fsencode (os .environ [os .fsdecode (var )])
396- else :
397- value = environ [var ]
398- except KeyError :
399- value = percent + var + percent
400- res += value
401- elif c == dollar : # variable or '$$'
402- if path [index + 1 :index + 2 ] == dollar :
403- res += c
404- index += 1
405- elif path [index + 1 :index + 2 ] == brace :
406- path = path [index + 2 :]
407- pathlen = len (path )
408- try :
409- index = path .index (rbrace )
410- except ValueError :
411- res += dollar + brace + path
412- index = pathlen - 1
413- else :
414- var = path [:index ]
415- try :
416- if environ is None :
417- value = os .fsencode (os .environ [os .fsdecode (var )])
418- else :
419- value = environ [var ]
420- except KeyError :
421- value = dollar + brace + var + rbrace
422- res += value
423- else :
424- var = path [:0 ]
425- index += 1
426- c = path [index :index + 1 ]
427- while c and c in varchars :
428- var += c
429- index += 1
430- c = path [index :index + 1 ]
431- try :
432- if environ is None :
433- value = os .fsencode (os .environ [os .fsdecode (var )])
434- else :
435- value = environ [var ]
436- except KeyError :
437- value = dollar + var
438- res += value
439- if c :
440- index -= 1
372+
373+ def repl (m ):
374+ lastindex = m .lastindex
375+ if lastindex is None :
376+ return m [0 ]
377+ name = m [lastindex ]
378+ if lastindex == 1 :
379+ if name == percent :
380+ return name
381+ if not name .endswith (percent ):
382+ return m [0 ]
383+ name = name [:- 1 ]
441384 else :
442- res += c
443- index += 1
444- return res
385+ if name == dollar :
386+ return name
387+ if name .startswith (brace ):
388+ if not name .endswith (rbrace ):
389+ return m [0 ]
390+ name = name [1 :- 1 ]
391+
392+ try :
393+ if environ is None :
394+ return os .fsencode (os .environ [os .fsdecode (name )])
395+ else :
396+ return environ [name ]
397+ except KeyError :
398+ return m [0 ]
399+
400+ return sub (repl , path )
445401
446402
447403# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
0 commit comments