Skip to content

Commit 7d8138f

Browse files
committed
Fix bug with incorrectly defactorized dependencies #706
New version with improved changes, fixing errors raised with the previous version: #899 #906
1 parent 8de2cef commit 7d8138f

File tree

4 files changed

+259
-4
lines changed

4 files changed

+259
-4
lines changed

docs/changelog/706.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix bug with incorrectly defactorized dependencies - by @bartsanchez

docs/config.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,31 @@ the following:
834834
``mysql-py36``,
835835
- but not ``py2``, ``py36-sql`` or ``py36-mysql-dev``.
836836

837+
Factors and values substitution are compatible
838+
++++++++++++++++++++++++++++++++++++++++++++++
839+
840+
It is possible to mix both values substitution and factor expressions.
841+
For example::
842+
843+
[tox]
844+
envlist = py27,py36,coverage
845+
846+
[testenv]
847+
deps =
848+
flake8
849+
coverage: coverage
850+
851+
[testenv:py27]
852+
deps =
853+
{{[testenv]deps}}
854+
pytest
855+
856+
With the previous configuration, it will install:
857+
858+
- ``flake8`` and ``pytest`` packages for ``py27`` environment.
859+
- ``flake8`` package for ``py36`` environment.
860+
- ``flake8`` and ``coverage`` packages for ``coverage`` environment.
861+
837862
Advanced settings
838863
-----------------
839864

src/tox/config.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1336,11 +1336,20 @@ def getstring(self, name, default=None, replace=True, crossonly=False):
13361336
if x is None:
13371337
x = default
13381338
else:
1339+
# It is needed to apply factors before unwrapping
1340+
# dependencies, otherwise it can break the substitution
1341+
# process. Once they are unwrapped, we call apply factors
1342+
# again for those new dependencies.
13391343
x = self._apply_factors(x)
1344+
x = self._replace_if_needed(x, name, replace, crossonly)
1345+
x = self._apply_factors(x)
1346+
1347+
x = self._replace_if_needed(x, name, replace, crossonly)
1348+
return x
13401349

1350+
def _replace_if_needed(self, x, name, replace, crossonly):
13411351
if replace and x and hasattr(x, "replace"):
13421352
x = self._replace(x, name=name, crossonly=crossonly)
1343-
# print "getstring", self.section_name, name, "returned", repr(x)
13441353
return x
13451354

13461355
def _apply_factors(self, s):

tests/unit/test_config.py

Lines changed: 223 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,11 +1444,9 @@ def test_take_dependencies_from_other_testenv(self, newconfig, envlist, deps):
14441444
)
14451445
conf = newconfig([], inisource).envconfigs["py27"]
14461446
packages = [dep.name for dep in conf.deps]
1447-
assert packages == list(deps) + ["fun", "frob>1.0,<2.0"]
1448-
# assert packages == ["pytest", "pytest-cov", "fun", "frob>1.0,<2.0"]
1447+
assert packages == ["pytest", "pytest-cov", "fun", "frob>1.0,<2.0"]
14491448

14501449
# https://github.com/tox-dev/tox/issues/706
1451-
@pytest.mark.xfail(reason="reproduce bug 706")
14521450
@pytest.mark.parametrize("envlist", [["py27", "coverage", "other"]])
14531451
def test_regression_test_issue_706(self, newconfig, envlist):
14541452
inisource = """
@@ -1477,6 +1475,228 @@ def test_regression_test_issue_706(self, newconfig, envlist):
14771475
packages = [dep.name for dep in conf.deps]
14781476
assert packages == ["flake8", "fun"]
14791477

1478+
def test_factor_expansion(self, newconfig):
1479+
inisource = """
1480+
[tox]
1481+
envlist = {py27, py37}-cover
1482+
[testenv]
1483+
deps=
1484+
{py27}: foo
1485+
{py37}: bar
1486+
"""
1487+
conf = newconfig([], inisource).envconfigs["py27-cover"]
1488+
packages = [dep.name for dep in conf.deps]
1489+
assert packages == ["foo"]
1490+
1491+
conf = newconfig([], inisource).envconfigs["py37-cover"]
1492+
packages = [dep.name for dep in conf.deps]
1493+
assert packages == ["bar"]
1494+
1495+
# Regression test https://github.com/tox-dev/tox/issues/899
1496+
def test_factors_support_curly_braces(self, newconfig):
1497+
inisource = """
1498+
[tox]
1499+
envlist =
1500+
style
1501+
sdist
1502+
bdist_wheel
1503+
{py27,py34,py35,py36,pypy,pypy3}-cover
1504+
{py27,py34,py35,py36,pypy,pypy3}-nocov
1505+
1506+
[testenv]
1507+
deps =
1508+
cover: coverage
1509+
cover: codecov
1510+
{py27}: unittest2
1511+
{py27}: mysql-python
1512+
{py27,py36}: mmtf-python
1513+
{py27,py35}: reportlab
1514+
{py27,py34,py35,py36}: psycopg2-binary
1515+
{py27,py34,py35,py35}: mysql-connector-python-rf
1516+
{py27,py35,pypy}: rdflib
1517+
{pypy,pypy3}: numpy==1.12.1
1518+
{py27,py34,py36}: numpy
1519+
{py36}: scipy
1520+
{py27}: networkx
1521+
{py36}: matplotlib
1522+
"""
1523+
conf = newconfig([], inisource).envconfigs["style"]
1524+
packages = [dep.name for dep in conf.deps]
1525+
assert packages == []
1526+
1527+
conf = newconfig([], inisource).envconfigs["py27-cover"]
1528+
packages = [dep.name for dep in conf.deps]
1529+
assert packages == [
1530+
"coverage",
1531+
"codecov",
1532+
"unittest2",
1533+
"mysql-python",
1534+
"mmtf-python",
1535+
"reportlab",
1536+
"psycopg2-binary",
1537+
"mysql-connector-python-rf",
1538+
"rdflib",
1539+
"numpy",
1540+
"networkx",
1541+
]
1542+
1543+
conf = newconfig([], inisource).envconfigs["py34-cover"]
1544+
packages = [dep.name for dep in conf.deps]
1545+
assert packages == [
1546+
"coverage",
1547+
"codecov",
1548+
"psycopg2-binary",
1549+
"mysql-connector-python-rf",
1550+
"numpy",
1551+
]
1552+
1553+
conf = newconfig([], inisource).envconfigs["py35-cover"]
1554+
packages = [dep.name for dep in conf.deps]
1555+
assert packages == [
1556+
"coverage",
1557+
"codecov",
1558+
"reportlab",
1559+
"psycopg2-binary",
1560+
"mysql-connector-python-rf",
1561+
"rdflib",
1562+
]
1563+
1564+
conf = newconfig([], inisource).envconfigs["py36-cover"]
1565+
packages = [dep.name for dep in conf.deps]
1566+
assert packages == [
1567+
"coverage",
1568+
"codecov",
1569+
"mmtf-python",
1570+
"psycopg2-binary",
1571+
"numpy",
1572+
"scipy",
1573+
"matplotlib",
1574+
]
1575+
1576+
conf = newconfig([], inisource).envconfigs["pypy-cover"]
1577+
packages = [dep.name for dep in conf.deps]
1578+
assert packages == ["coverage", "codecov", "rdflib", "numpy==1.12.1"]
1579+
1580+
conf = newconfig([], inisource).envconfigs["pypy3-cover"]
1581+
packages = [dep.name for dep in conf.deps]
1582+
assert packages == ["coverage", "codecov", "numpy==1.12.1"]
1583+
1584+
conf = newconfig([], inisource).envconfigs["py27-nocov"]
1585+
packages = [dep.name for dep in conf.deps]
1586+
assert packages == [
1587+
"unittest2",
1588+
"mysql-python",
1589+
"mmtf-python",
1590+
"reportlab",
1591+
"psycopg2-binary",
1592+
"mysql-connector-python-rf",
1593+
"rdflib",
1594+
"numpy",
1595+
"networkx",
1596+
]
1597+
1598+
conf = newconfig([], inisource).envconfigs["py34-nocov"]
1599+
packages = [dep.name for dep in conf.deps]
1600+
assert packages == ["psycopg2-binary", "mysql-connector-python-rf", "numpy"]
1601+
1602+
conf = newconfig([], inisource).envconfigs["py35-nocov"]
1603+
packages = [dep.name for dep in conf.deps]
1604+
assert packages == ["reportlab", "psycopg2-binary", "mysql-connector-python-rf", "rdflib"]
1605+
1606+
conf = newconfig([], inisource).envconfigs["py36-nocov"]
1607+
packages = [dep.name for dep in conf.deps]
1608+
assert packages == ["mmtf-python", "psycopg2-binary", "numpy", "scipy", "matplotlib"]
1609+
1610+
conf = newconfig([], inisource).envconfigs["pypy-nocov"]
1611+
packages = [dep.name for dep in conf.deps]
1612+
assert packages == ["rdflib", "numpy==1.12.1"]
1613+
1614+
conf = newconfig([], inisource).envconfigs["pypy3-cover"]
1615+
packages = [dep.name for dep in conf.deps]
1616+
assert packages == ["coverage", "codecov", "numpy==1.12.1"]
1617+
1618+
# Regression test https://github.com/tox-dev/tox/issues/906
1619+
def test_do_not_substitute_more_than_needed(self, newconfig):
1620+
inisource = """
1621+
[tox]
1622+
envlist =
1623+
django_master-py{36,35}
1624+
django20-py{36,35,34,py3}
1625+
django111-py{36,35,34,27,py}
1626+
django18-py{35,34,27,py}
1627+
lint
1628+
docs
1629+
1630+
[testenv]
1631+
deps =
1632+
.[test]
1633+
django18: {[django]1.8.x}
1634+
django111: {[django]1.11.x}
1635+
django20: {[django]2.0.x}
1636+
django_master: {[django]master}
1637+
1638+
[django]
1639+
1.8.x =
1640+
Django>=1.8.0,<1.9.0
1641+
django-reversion==1.10.0
1642+
djangorestframework>=3.3.3,<3.7.0
1643+
1.11.x =
1644+
Django>=1.11.0,<2.0.0
1645+
django-reversion>=2.0.8
1646+
djangorestframework>=3.6.2
1647+
2.0.x =
1648+
Django>=2.0,<2.1
1649+
django-reversion>=2.0.8
1650+
djangorestframework>=3.7.3
1651+
master =
1652+
https://github.com/django/django/tarball/master
1653+
django-reversion>=2.0.8
1654+
djangorestframework>=3.6.2
1655+
"""
1656+
conf = newconfig([], inisource).envconfigs["django_master-py36"]
1657+
packages = [dep.name for dep in conf.deps]
1658+
assert packages == [
1659+
".[test]",
1660+
"https://github.com/django/django/tarball/master",
1661+
"django-reversion>=2.0.8",
1662+
"djangorestframework>=3.6.2",
1663+
]
1664+
1665+
conf = newconfig([], inisource).envconfigs["django20-pypy3"]
1666+
packages = [dep.name for dep in conf.deps]
1667+
assert packages == [
1668+
".[test]",
1669+
"Django>=2.0,<2.1",
1670+
"django-reversion>=2.0.8",
1671+
"djangorestframework>=3.7.3",
1672+
]
1673+
1674+
conf = newconfig([], inisource).envconfigs["django111-py34"]
1675+
packages = [dep.name for dep in conf.deps]
1676+
assert packages == [
1677+
".[test]",
1678+
"Django>=1.11.0,<2.0.0",
1679+
"django-reversion>=2.0.8",
1680+
"djangorestframework>=3.6.2",
1681+
]
1682+
1683+
conf = newconfig([], inisource).envconfigs["django18-py27"]
1684+
packages = [dep.name for dep in conf.deps]
1685+
assert packages == [
1686+
".[test]",
1687+
"Django>=1.8.0,<1.9.0",
1688+
"django-reversion==1.10.0",
1689+
"djangorestframework>=3.3.3,<3.7.0",
1690+
]
1691+
1692+
conf = newconfig([], inisource).envconfigs["lint"]
1693+
packages = [dep.name for dep in conf.deps]
1694+
assert packages == [".[test]"]
1695+
1696+
conf = newconfig([], inisource).envconfigs["docs"]
1697+
packages = [dep.name for dep in conf.deps]
1698+
assert packages == [".[test]"]
1699+
14801700
def test_take_dependencies_from_other_section(self, newconfig):
14811701
inisource = """
14821702
[testing:pytest]

0 commit comments

Comments
 (0)