@@ -400,17 +400,23 @@ def expanduser(path):
400400# XXX With COMMAND.COM you can use any characters in a variable name,
401401# XXX except '^|<>='.
402402
403+ _varpattern = r"'[^']*'?|%(%|[^%]*%?)|\$(\$|[-\w]+|\{[^}]*\}?)"
404+ _varsub = None
405+ _varsubb = None
406+
403407def expandvars (path ):
404408 """Expand shell variables of the forms $var, ${var} and %var%.
405409
406410 Unknown variables are left unchanged."""
407411 path = os .fspath (path )
412+ global _varsub , _varsubb
408413 if isinstance (path , bytes ):
409414 if b'$' not in path and b'%' not in path :
410415 return path
411- import string
412- varchars = bytes (string .ascii_letters + string .digits + '_-' , 'ascii' )
413- quote = b'\' '
416+ if not _varsubb :
417+ import re
418+ _varsubb = re .compile (_varpattern .encode (), re .ASCII ).sub
419+ sub = _varsubb
414420 percent = b'%'
415421 brace = b'{'
416422 rbrace = b'}'
@@ -419,94 +425,44 @@ def expandvars(path):
419425 else :
420426 if '$' not in path and '%' not in path :
421427 return path
422- import string
423- varchars = string .ascii_letters + string .digits + '_-'
424- quote = '\' '
428+ if not _varsub :
429+ import re
430+ _varsub = re .compile (_varpattern , re .ASCII ).sub
431+ sub = _varsub
425432 percent = '%'
426433 brace = '{'
427434 rbrace = '}'
428435 dollar = '$'
429436 environ = os .environ
430- res = path [:0 ]
431- index = 0
432- pathlen = len (path )
433- while index < pathlen :
434- c = path [index :index + 1 ]
435- if c == quote : # no expansion within single quotes
436- path = path [index + 1 :]
437- pathlen = len (path )
438- try :
439- index = path .index (c )
440- res += c + path [:index + 1 ]
441- except ValueError :
442- res += c + path
443- index = pathlen - 1
444- elif c == percent : # variable or '%'
445- if path [index + 1 :index + 2 ] == percent :
446- res += c
447- index += 1
448- else :
449- path = path [index + 1 :]
450- pathlen = len (path )
451- try :
452- index = path .index (percent )
453- except ValueError :
454- res += percent + path
455- index = pathlen - 1
456- else :
457- var = path [:index ]
458- try :
459- if environ is None :
460- value = os .fsencode (os .environ [os .fsdecode (var )])
461- else :
462- value = environ [var ]
463- except KeyError :
464- value = percent + var + percent
465- res += value
466- elif c == dollar : # variable or '$$'
467- if path [index + 1 :index + 2 ] == dollar :
468- res += c
469- index += 1
470- elif path [index + 1 :index + 2 ] == brace :
471- path = path [index + 2 :]
472- pathlen = len (path )
473- try :
474- index = path .index (rbrace )
475- except ValueError :
476- res += dollar + brace + path
477- index = pathlen - 1
478- else :
479- var = path [:index ]
480- try :
481- if environ is None :
482- value = os .fsencode (os .environ [os .fsdecode (var )])
483- else :
484- value = environ [var ]
485- except KeyError :
486- value = dollar + brace + var + rbrace
487- res += value
488- else :
489- var = path [:0 ]
490- index += 1
491- c = path [index :index + 1 ]
492- while c and c in varchars :
493- var += c
494- index += 1
495- c = path [index :index + 1 ]
496- try :
497- if environ is None :
498- value = os .fsencode (os .environ [os .fsdecode (var )])
499- else :
500- value = environ [var ]
501- except KeyError :
502- value = dollar + var
503- res += value
504- if c :
505- index -= 1
437+
438+ def repl (m ):
439+ lastindex = m .lastindex
440+ if lastindex is None :
441+ return m [0 ]
442+ name = m [lastindex ]
443+ if lastindex == 1 :
444+ if name == percent :
445+ return name
446+ if not name .endswith (percent ):
447+ return m [0 ]
448+ name = name [:- 1 ]
506449 else :
507- res += c
508- index += 1
509- return res
450+ if name == dollar :
451+ return name
452+ if name .startswith (brace ):
453+ if not name .endswith (rbrace ):
454+ return m [0 ]
455+ name = name [1 :- 1 ]
456+
457+ try :
458+ if environ is None :
459+ return os .fsencode (os .environ [os .fsdecode (name )])
460+ else :
461+ return environ [name ]
462+ except KeyError :
463+ return m [0 ]
464+
465+ return sub (repl , path )
510466
511467
512468# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A\B.
0 commit comments