18
18
import pluggy
19
19
import py
20
20
import toml
21
+ from py ._path .common import PathBase
21
22
from packaging import requirements
22
23
from packaging .utils import canonicalize_name
23
24
@@ -390,7 +391,7 @@ def get(self, name, default=None):
390
391
return os .environ .get (name , default )
391
392
self ._lookupstack .append (name )
392
393
try :
393
- res = self .reader ._replace (val )
394
+ res = self .reader ._replace (val , unquote_path = False )
394
395
res = res .replace ("\\ {" , "{" ).replace ("\\ }" , "}" )
395
396
self .resolved [name ] = res
396
397
finally :
@@ -1591,7 +1592,7 @@ def addsubstitutions(self, _posargs=None, **kw):
1591
1592
self .posargs = _posargs
1592
1593
1593
1594
def getpath (self , name , defaultpath , replace = True ):
1594
- path = self .getstring (name , defaultpath , replace = replace )
1595
+ path = self .getstring (name , defaultpath , replace = replace , unquote_path = True )
1595
1596
if path is not None :
1596
1597
toxinidir = self ._subs ["toxinidir" ]
1597
1598
return toxinidir .join (path , abs = True )
@@ -1680,7 +1681,15 @@ def getargv_install_command(self, name, default="", replace=True):
1680
1681
1681
1682
return _ArgvlistReader .getargvlist (self , s , replace = replace )[0 ]
1682
1683
1683
- def getstring (self , name , default = None , replace = True , crossonly = False , no_fallback = False ):
1684
+ def getstring (
1685
+ self ,
1686
+ name ,
1687
+ default = None ,
1688
+ replace = True ,
1689
+ crossonly = False ,
1690
+ no_fallback = False ,
1691
+ unquote_path = False ,
1692
+ ):
1684
1693
x = None
1685
1694
sections = [self .section_name ] + ([] if no_fallback else self .fallbacksections )
1686
1695
for s in sections :
@@ -1698,10 +1707,10 @@ def getstring(self, name, default=None, replace=True, crossonly=False, no_fallba
1698
1707
# process. Once they are unwrapped, we call apply factors
1699
1708
# again for those new dependencies.
1700
1709
x = self ._apply_factors (x )
1701
- x = self ._replace_if_needed (x , name , replace , crossonly )
1710
+ x = self ._replace_if_needed (x , name , replace , crossonly , unquote_path = unquote_path )
1702
1711
x = self ._apply_factors (x )
1703
1712
1704
- x = self ._replace_if_needed (x , name , replace , crossonly )
1713
+ x = self ._replace_if_needed (x , name , replace , crossonly , unquote_path = unquote_path )
1705
1714
return x
1706
1715
1707
1716
def getposargs (self , default = None ):
@@ -1715,9 +1724,9 @@ def getposargs(self, default=None):
1715
1724
else :
1716
1725
return default or ""
1717
1726
1718
- def _replace_if_needed (self , x , name , replace , crossonly ):
1727
+ def _replace_if_needed (self , x , name , replace , crossonly , unquote_path = False ):
1719
1728
if replace and x and hasattr (x , "replace" ):
1720
- x = self ._replace (x , name = name , crossonly = crossonly )
1729
+ x = self ._replace (x , name = name , crossonly = crossonly , unquote_path = unquote_path )
1721
1730
return x
1722
1731
1723
1732
def _apply_factors (self , s ):
@@ -1736,14 +1745,17 @@ def factor_line(line):
1736
1745
lines = s .strip ().splitlines ()
1737
1746
return "\n " .join (filter (None , map (factor_line , lines )))
1738
1747
1739
- def _replace (self , value , name = None , section_name = None , crossonly = False ):
1748
+ def _replace (self , value , name = None , section_name = None , crossonly = False , unquote_path = False ):
1740
1749
if "{" not in value :
1741
1750
return value
1742
1751
1743
1752
section_name = section_name if section_name else self .section_name
1744
1753
self ._subststack .append ((section_name , name ))
1745
1754
try :
1746
- replaced = Replacer (self , crossonly = crossonly ).do_replace (value )
1755
+ replacer = Replacer (self , crossonly = crossonly )
1756
+ replaced = replacer .do_replace (value )
1757
+ if unquote_path and replacer ._path_quoted :
1758
+ replaced = replaced .strip ("'" )
1747
1759
assert self ._subststack .pop () == (section_name , name )
1748
1760
except tox .exception .MissingSubstitution :
1749
1761
if not section_name .startswith (testenvprefix ):
@@ -1770,6 +1782,7 @@ class Replacer:
1770
1782
def __init__ (self , reader , crossonly = False ):
1771
1783
self .reader = reader
1772
1784
self .crossonly = crossonly
1785
+ self ._path_quoted = False
1773
1786
1774
1787
def do_replace (self , value ):
1775
1788
"""
@@ -1853,6 +1866,7 @@ def _substitute_from_other_section(self, key):
1853
1866
name = item ,
1854
1867
section_name = section ,
1855
1868
crossonly = self .crossonly ,
1869
+ unquote_path = False ,
1856
1870
)
1857
1871
1858
1872
raise tox .exception .ConfigError ("substitution key {!r} not found" .format (key ))
@@ -1864,6 +1878,11 @@ def _replace_substitution(self, match):
1864
1878
val = self ._substitute_from_other_section (sub_key )
1865
1879
if callable (val ):
1866
1880
val = val ()
1881
+ if isinstance (val , PathBase ):
1882
+ val = str (val )
1883
+ if "#" in val :
1884
+ val = "'{}'" .format (val )
1885
+ self ._path_quoted = True
1867
1886
return str (val )
1868
1887
1869
1888
@@ -1895,7 +1914,7 @@ def getargvlist(cls, reader, value, replace=True):
1895
1914
current_command += line
1896
1915
1897
1916
if is_section_substitution (current_command ):
1898
- replaced = reader ._replace (current_command , crossonly = True )
1917
+ replaced = reader ._replace (current_command , crossonly = True , unquote_path = False )
1899
1918
commands .extend (cls .getargvlist (reader , replaced ))
1900
1919
else :
1901
1920
commands .append (cls .processcommand (reader , current_command , replace ))
@@ -1924,8 +1943,8 @@ def processcommand(cls, reader, command, replace=True):
1924
1943
continue
1925
1944
1926
1945
new_arg = ""
1927
- new_word = reader ._replace (word )
1928
- new_word = reader ._replace (new_word )
1946
+ new_word = reader ._replace (word , unquote_path = False )
1947
+ new_word = reader ._replace (new_word , unquote_path = False )
1929
1948
new_word = new_word .replace ("\\ {" , "{" ).replace ("\\ }" , "}" )
1930
1949
new_arg += new_word
1931
1950
newcommand += new_arg
0 commit comments