Skip to content

Commit

Permalink
Merge pull request #581 from opencobra/fix-pytest
Browse files Browse the repository at this point in the history
upgrade memote to be pytest 4.x compatible
  • Loading branch information
Midnighter committed Jan 18, 2019
2 parents 32d7722 + 02dec44 commit 128e063
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 81 deletions.
3 changes: 3 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ History

Next Release
------------
* Unpin pytest (require >= 4.0) and adjust some internal mechanics accordingly.
* Display an alternative message if some biomass components do not contain a
formula.

0.8.11 (2019-01-07)
-------------------
Expand Down
44 changes: 20 additions & 24 deletions memote/suite/collect.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,26 @@ def __init__(self, model, experimental_config=None,
self._xcld = frozenset() if exclusive is None else frozenset(exclusive)
self._skip = frozenset() if skip is None else frozenset(skip)

def pytest_namespace(self):
"""Insert model information into the pytest namespace."""
namespace = dict()
namespace["memote"] = memote = dict()
memote["biomass_ids"] = [
rxn.id for rxn in find_biomass_reaction(self._model)]
memote["compartment_ids"] = sorted(self._model.compartments)
try:
memote["compartment_ids"].remove("c")
except ValueError:
LOGGER.error(
"The model does not contain a compartment ID labeled 'c' for "
"the cytosol which is an essential compartment. Many syntax "
"tests depend on this being labeled accordingly.")
# Add experimental information if there are any.
if self._exp_config is None:
memote["experimental"] = dict()
memote["experimental"]["essentiality"] = list()
memote["experimental"]["growth"] = list()
else:
# Load experimental data.
self._exp_config.load_data(self._model)
memote["experimental"] = self._exp_config
return namespace
def pytest_generate_tests(self, metafunc):
"""Parametrize marked functions at runtime."""
if metafunc.definition.get_closest_marker("biomass"):
metafunc.parametrize("reaction_id", [
rxn.id for rxn in find_biomass_reaction(self._model)])
return
if metafunc.definition.get_closest_marker("essentiality"):
if self._exp_config is None:
metafunc.parametrize("experiment", [])
else:
metafunc.parametrize("experiment",
self._exp_config.get("essentiality", []))
return
if metafunc.definition.get_closest_marker("growth"):
if self._exp_config is None:
metafunc.parametrize("experiment", [])
else:
metafunc.parametrize("experiment",
self._exp_config.get("growth", []))
return

@pytest.hookimpl(tryfirst=True)
def pytest_runtest_call(self, item):
Expand Down
50 changes: 29 additions & 21 deletions memote/suite/tests/test_biomass.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,10 @@


LOGGER = logging.getLogger(__name__)
BIOMASS_IDS = pytest.memote.biomass_ids


@annotate(title="Biomass Reactions Identified", format_type="count")
def test_biomass_presence():
def test_biomass_presence(read_only_model):
"""
Expect the model to contain at least one biomass reaction.
Expand All @@ -57,16 +56,16 @@ def test_biomass_presence():
future.
"""
ann = test_biomass_presence.annotation
ann["data"] = BIOMASS_IDS
ann["data"] = [
rxn.id for rxn in helpers.find_biomass_reaction(read_only_model)]
ann["message"] = wrapper.fill(
"""In this model {} the following biomass reactions were
identified: {}""".format(
len(ann["data"]), truncate(ann["data"])))
assert len(ann["data"]) > 0, ann["message"]


# TODO: Mark this test as skipped if the biomass components lack formulas!
@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Biomass Consistency", format_type="number", data=dict(),
message=dict(), metric=dict())
def test_biomass_consistency(read_only_model, reaction_id):
Expand All @@ -79,23 +78,32 @@ def test_biomass_consistency(read_only_model, reaction_id):
defined to be equal to 1 g/mmol. Conforming to this is essential in order
to be able to reliably calculate growth yields, to cross-compare models,
and to obtain valid predictions when simulating microbial consortia. A
deviation by 1e-03 is accepted.
deviation from 1 - 1E-03 to 1 + 1E-06 is accepted.
"""
ann = test_biomass_consistency.annotation
reaction = read_only_model.reactions.get_by_id(reaction_id)
ann["data"][reaction_id] = biomass.sum_biomass_weight(reaction)
try:
ann["data"][reaction_id] = biomass.sum_biomass_weight(reaction)
except TypeError:
ann["data"][reaction_id] = float("nan")
ann["message"][reaction_id] = wrapper.fill(
"""One or more of the biomass components do not have a defined
formula or contain unspecified chemical groups."""
)
else:
ann["message"][reaction_id] = wrapper.fill(
"""The component molar mass of the biomass reaction {} sums up to {}
which is outside of the 1e-03 margin from 1 mmol / g[CDW] / h.
""".format(reaction_id, ann["data"][reaction_id])
)
ann["metric"][reaction_id] = 1.0 # Placeholder value.
ann["message"][reaction_id] = wrapper.fill(
"""The component molar mass of the biomass reaction {} sums up to {}
which is outside of the 1e-03 margin from 1 mmol / g[CDW] / h.
""".format(reaction_id, ann["data"][reaction_id]))
# To account for numerical innacuracies, a range from 1-1e0-3 to 1+1e-06
# is implemented in the assertion check
# To account for numerical inaccuracies, a range from 1 - 1e0-3 to 1 + 1e-06
# is implemented in the assertion check.
assert (1 - 1e-03) < ann["data"][reaction_id] < (1 + 1e-06), \
ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Biomass Production In Default Medium", format_type="number",
data=dict(), message=dict(), metric=dict())
def test_biomass_default_production(model, reaction_id):
Expand All @@ -117,7 +125,7 @@ def test_biomass_default_production(model, reaction_id):
assert ann["data"][reaction_id] > 0.0, ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Biomass Production In Complete Medium", format_type="number",
data=dict(), message=dict(), metric=dict())
def test_biomass_open_production(model, reaction_id):
Expand All @@ -140,7 +148,7 @@ def test_biomass_open_production(model, reaction_id):
assert ann["data"][reaction_id] > 0.0, ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Blocked Biomass Precursors In Default Medium",
format_type="count", data=dict(), metric=dict(), message=dict())
def test_biomass_precursors_default_production(read_only_model, reaction_id):
Expand Down Expand Up @@ -173,7 +181,7 @@ def test_biomass_precursors_default_production(read_only_model, reaction_id):
assert len(ann["data"][reaction_id]) == 0, ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Blocked Biomass Precursors In Complete Medium",
format_type="count", data=dict(), metric=dict(), message=dict())
def test_biomass_precursors_open_production(model, reaction_id):
Expand Down Expand Up @@ -207,7 +215,7 @@ def test_biomass_precursors_open_production(model, reaction_id):
assert len(ann["data"][reaction_id]) == 0, ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Growth-associated Maintenance in Biomass Reaction",
format_type="raw", data=dict(), message=dict(), metric=dict())
def test_gam_in_biomass(model, reaction_id):
Expand All @@ -229,7 +237,7 @@ def test_gam_in_biomass(model, reaction_id):
assert ann["data"][reaction_id], ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Unrealistic Growth Rate In Default Medium",
format_type="raw", data=dict(), message=dict(), metric=dict())
def test_fast_growth_default(model, reaction_id):
Expand All @@ -251,7 +259,7 @@ def test_fast_growth_default(model, reaction_id):
assert ann["data"][reaction_id] <= 10.3972, ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Ratio of Direct Metabolites in Biomass Reaction",
format_type="percent", data=dict(), message=dict(), metric=dict())
def test_direct_metabolites_in_biomass(model, reaction_id):
Expand Down Expand Up @@ -303,7 +311,7 @@ def test_direct_metabolites_in_biomass(model, reaction_id):
assert ann["metric"][reaction_id] < 0.5, ann["message"][reaction_id]


@pytest.mark.parametrize("reaction_id", BIOMASS_IDS)
@pytest.mark.biomass
@annotate(title="Number of Missing Essential Biomass Precursors",
format_type="count", data=dict(), message=dict(), metric=dict())
def test_essential_precursors_not_in_biomass(model, reaction_id):
Expand Down
6 changes: 1 addition & 5 deletions memote/suite/tests/test_essentiality.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,8 @@
from memote.utils import annotate, wrapper
from memote.support.essentiality import confusion_matrix

ESSENTIALITY_DATA = list(pytest.memote.experimental.essentiality)


@pytest.mark.skipif(len(ESSENTIALITY_DATA) == 0,
reason="No essentiality data found.")
@pytest.mark.parametrize("experiment", ESSENTIALITY_DATA)
@pytest.mark.essentiality
@annotate(title="Gene Essentiality Prediction", format_type="percent",
data=dict(), message=dict(), metric=dict())
def test_gene_essentiality_from_data_qualitative(model, experiment,
Expand Down
6 changes: 1 addition & 5 deletions memote/suite/tests/test_growth.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,8 @@
from memote.utils import annotate, wrapper
from memote.support.essentiality import confusion_matrix

GROWTH_DATA = list(pytest.memote.experimental.growth)


@pytest.mark.skipif(len(GROWTH_DATA) == 0,
reason="No growth data found.")
@pytest.mark.parametrize("experiment", GROWTH_DATA)
@pytest.mark.growth
@annotate(title="Growth Prediction", format_type="percent",
data=dict(), message=dict(), metric=dict())
def test_growth_from_data_qualitative(model, experiment, threshold=0.95):
Expand Down
35 changes: 15 additions & 20 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ parse = (?P<major>\d+)
\.(?P<minor>\d+)
\.(?P<patch>\d+)
(?P<release>[a]?)(?P<num>\d*)
serialize =
serialize =
{major}.{minor}.{patch}{release}{num}
{major}.{minor}.{patch}

Expand All @@ -17,7 +17,7 @@ url = https://github.com/opencobra/memote
download_url = https://pypi.org/pypi/memote/
author = Moritz E. Beber
author_email = morbeb@biosustain.dtu.dk
classifiers =
classifiers =
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
Intended Audience :: Science/Research
Expand All @@ -33,7 +33,7 @@ classifiers =
license = Apache Software License Version 2.0
description = the genome-scale metabolic model test suite
long_description = file: README.rst
keywords =
keywords =
memote
metabolic
constrained-based
Expand All @@ -45,13 +45,13 @@ keywords =

[options]
zip_safe = False
install_requires =
install_requires =
click <7.0
click-configfile
click-log
six
future
pytest >=3.1,<4.1
pytest >=4.0
gitpython
pandas >=0.20.1
Jinja2
Expand All @@ -72,35 +72,30 @@ install_requires =
depinfo
equilibrator_api;python_version>='3.5'
python_requires = >=2.7,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*
tests_require =
pytest>=3.1
pytest-raises
pytest-cov
codecov
xlwt
XlsxWriter
tests_require =
tox
packages = find:

[options.package_data]
memote.experimental.schemata =
memote.experimental.schemata =
*.json
memote.suite =
memote.suite =
tests/*.py
memote.suite.templates =
memote.suite.templates =
*.html
*.yml
memote.support.data =
memote.support.data =
*.csv
*.json

[options.entry_points]
console_scripts =
console_scripts =
memote = memote.suite.cli.runner:cli

[bumpversion:part:release]
optional_value = placeholder
first_value = placeholder
values =
values =
placeholder
a

Expand Down Expand Up @@ -128,7 +123,7 @@ universal = 1

[flake8]
max-line-length = 80
exclude =
exclude =
__init__.py
docs

Expand All @@ -137,7 +132,7 @@ match_dir = memote

[tool:pytest]
testpaths = tests
filterwarnings =
filterwarnings =
ignore::DeprecationWarning:libsbml

[isort]
Expand Down
6 changes: 4 additions & 2 deletions tests/test_for_experimental/test_for_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ def test_configuration_schema():


@pytest.mark.parametrize("filename", [
pytest.mark.raises("empty.yml", exception=ValidationError),
pytest.mark.raises("invalid.yml", exception=ValidationError),
pytest.param("empty.yml",
marks=pytest.mark.raises(exception=ValidationError)),
pytest.param("invalid.yml",
marks=pytest.mark.raises(exception=ValidationError)),
"valid.yml",
"medium_only.yml"
])
Expand Down
3 changes: 2 additions & 1 deletion tests/test_for_suite/test_for_results/test_for_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ def test_insert(self, session):

@pytest.mark.parametrize("name_a, name_b", [
("abcdef", "ghijkl"),
pytest.mark.raises(("same", "same"), exception=IntegrityError)
pytest.param("same", "same",
marks=pytest.mark.raises(exception=IntegrityError))
])
def test_unique_hexsha(self, session, name_a, name_b):
timestamp = datetime.now()
Expand Down
22 changes: 22 additions & 0 deletions tests/test_for_support/test_for_biomass.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,27 @@ def sum_outside_of_deviation(base):
return base


@register_with(MODEL_REGISTRY)
def sum_missing_formula(base):
"""Define a biomass reaction whose components lack a formula."""
met_a = cobra.Metabolite("lipid_c", "H744")
met_b = cobra.Metabolite("protein_c", "H119")
met_c = cobra.Metabolite("rna_c")
met_d = cobra.Metabolite("dna_c")
met_e = cobra.Metabolite("ash_c", None)
# Reactions
rxn_1 = cobra.Reaction("BIOMASS_TEST")
rxn_1.add_metabolites({
met_a: -0.2,
met_b: -0.2,
met_c: -0.2,
met_d: -0.2,
met_e: -0.2
})
base.add_reactions([rxn_1])
return base


@register_with(MODEL_REGISTRY)
def precursors_producing(base):
met_a = cobra.Metabolite("lipid_c", compartment='c')
Expand Down Expand Up @@ -474,6 +495,7 @@ def essential_not_in_model(base):
@pytest.mark.parametrize("model, expected", [
("sum_within_deviation", True),
("sum_outside_of_deviation", False),
("sum_missing_formula", False),
], indirect=["model"])
def test_biomass_weight_production(model, expected):
"""
Expand Down

0 comments on commit 128e063

Please sign in to comment.