Commit 4292ad33 authored by Elad Noor's avatar Elad Noor

moving to pytest and tox

parent 6d9ef874
{}
\ No newline at end of file
[
"tests/test_unit/test_api.py::test_atp_hydrolysis",
"tests/test_unit/test_api.py::test_reaction_balancing",
"tests/test_unit/test_api.py::test_gibbs_energy",
"tests/test_unit/test_api.py::test_reduction_potential",
"tests/test_unit/test_api.py::test_mdf",
"tests/test_unit/test_api.py::test_reaction_matcher"
]
\ No newline at end of file
.. highlight:: shell
============
Contributing
============
Contributions are welcome, and they are greatly appreciated! Every little bit
helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions
======================
Report Bugs
-----------
Report bugs at https://gitlab.com/elad.noor/equilibrator-api/issues.
If you are reporting a bug, please follow the presented issue template since it
is designed to ultimately make helping you easier and thus faster.
Write Documentation
-------------------
As any open source project, equilibrator-api could always use more and
better documentation, whether as part of the official docs, in docstrings, or
even on the web in blog posts, articles.
Submit Feedback
---------------
The best way to send feedback is to file an issue at
https://gitlab.com/elad.noor/equilibrator-api/issues.
If you are proposing a feature:
* Explain in detail how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that contributions
are welcome ;)
Get Started!
------------
Ready to contribute? Here's how to set up ``equilibrator-api`` for local
development.
1. Install `git-lfs <https://git-lfs.github.com/>`__. On many linux
distributions this can be installed from official package managers. 2. Fork
the ``equilibrator-api`` repo on GitLab. 3. Clone your fork locally::
git clone [email protected]:<your_name_here>/equilibrator-api.git
4. Create a branch for local development::
git checkout -b name-of-your-bugfix-or-feature-branch
Now you can make your changes locally.
5. When you're done making changes, check that your changes pass the quality
control::
tox
To get tox, just pip install it.
6. Commit your changes using `semantic commit messages
<https://seesparkbox.com/foundry/semantic_commit_messages>`__ and push your
branch to GitHub::
git add . git commit -m "feat: your detailed description of your changes"
git push origin name-of-your-bugfix-or-feature
7. Submit a pull request to this repository through the GitHub website.
The MIT License (MIT)
Copyright (c) 2013 Weizmann Institute of Science
Copyright (c) 2018 Institute for Molecular Systems Biology,
ETH Zurich
Copyright (c) 2018 Novo Nordisk Foundation Center for Biosustainability,
Technical University of Denmark
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
recursive-include equilibrator_api/data *.csv *.npz *.json *.tsv
include versioneer.py
include component_contribution/_version.py
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 10 17:22:11 2017
@author: noore
"""
import unittest
import os
import warnings
import inspect
from equilibrator_api import Reaction, ComponentContribution, ReactionMatcher
from equilibrator_api import Pathway
TEST_DIR = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
class TestReactionParsing(unittest.TestCase):
def test_atp_hydrolysis(self):
warnings.simplefilter('ignore', ResourceWarning)
formula = ' C00002 + C00001 <= C00008 + C00009'
kegg_ids = set(('C00002', 'C00001', 'C00008', 'C00009'))
try:
reaction = Reaction.parse_formula(formula)
except ValueError as e:
self.fail('unable to parse the formula\n' + str(e))
self.assertSetEqual(set(reaction.kegg_ids), kegg_ids)
for kegg_id in kegg_ids:
self.assertIsNotNone(reaction.get_compound(kegg_id))
self.assertNotEqual(reaction.get_coeff(kegg_id), 0)
self.assertIsNone(reaction.get_compound('C00003'))
self.assertEqual(reaction.get_coeff('C00003'), 0)
def test_reaction_balancing(self):
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00011' : -1, 'C00001' : -1, 'C01353' : 1}
reaction = Reaction(kegg_id_to_coeff)
self.assertTrue(reaction.check_full_reaction_balancing())
kegg_id_to_coeff = {'C00036' : -1, 'C00149' : 1} # oxaloacetate = malate
reaction = Reaction(kegg_id_to_coeff)
self.assertAlmostEqual(reaction.check_half_reaction_balancing(), 2.0)
kegg_id_to_coeff = {'C00031' : -1, 'C00469' : 2} # missing two CO2
reaction = Reaction(kegg_id_to_coeff)
self.assertDictEqual(reaction._get_reaction_atom_balance(),
{'O': -4, 'C': -2, 'e-': -44})
def test_gibbs_energy(self):
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00002' : -1, 'C00001' : -1,
'C00008' : 1, 'C00009' : 1} # ATP + H2O = ADP + Pi
kegg_id_to_conc = {'C00002' : 1e-3,
'C00009' : 1e-4}
reaction = Reaction(kegg_id_to_coeff)
cc = ComponentContribution(pH=7.0, ionic_strength=0.1)
dG0_prime, dG0_uncertainty = cc.dG0_prime(reaction)
self.assertAlmostEqual(dG0_prime, -26.4, 1)
self.assertAlmostEqual(dG0_uncertainty, 0.6, 1)
dG_prime, _ = cc.dG_prime(reaction, kegg_id_to_conc)
self.assertAlmostEqual(dG_prime, -32.1, 1)
dGm_prime, _ = cc.dGm_prime(reaction)
self.assertAlmostEqual(dGm_prime, -43.5, 1)
def test_reduction_potential(self):
warnings.simplefilter('ignore', ResourceWarning)
kegg_id_to_coeff = {'C00036' : -1, 'C00149' : 1} # oxaloacetate = malate
reaction = Reaction(kegg_id_to_coeff)
cc = ComponentContribution(pH=7.0, ionic_strength=0.1)
E0_prime_mV, E0_uncertainty = cc.E0_prime(reaction)
self.assertAlmostEqual(E0_prime_mV, -175.2, 1)
self.assertAlmostEqual(E0_uncertainty, 5.3, 1)
def test_mdf(self):
warnings.simplefilter('ignore', ResourceWarning)
sbtab_fname = os.path.join(TEST_DIR, 'pathway_ethanol_SBtab.tsv')
pp = Pathway.from_sbtab(sbtab_fname)
mdf_res = pp.calc_mdf().mdf_result
self.assertAlmostEqual(mdf_res.mdf, 1.69, 2)
self.assertAlmostEqual(mdf_res.max_total_dG, -159.05, 2)
self.assertAlmostEqual(mdf_res.min_total_dG, -181.05, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[0, 0], 0.0, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[3, 0], 0.25, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[4, 0], 0.25, 2)
self.assertAlmostEqual(mdf_res.reaction_prices[5, 0], 0.50, 2)
self.assertAlmostEqual(mdf_res.compound_prices[1, 0], 0.0, 2)
self.assertAlmostEqual(mdf_res.compound_prices[2, 0], 1.24, 2)
self.assertAlmostEqual(mdf_res.compound_prices[3, 0], -1.24, 2)
def test_reaction_matcher(self):
warnings.simplefilter('ignore', ResourceWarning)
formulas = [('ATP + H2O <=> ADP + Phosphate',
{'C00002': -1, 'C00001': -1, 'C00008': 1, 'C00009': 1}),
('O2 + 2 NADH <=> 2 NAD+ + 2 H2O',
{'C00007': -1, 'C00004': -2, 'C00003': 2, 'C00001': 2}),
('O2 + 2 NADH <=> 2 NAD+ + 2 H2O',
{'C00007': -1, 'C00004': -2, 'C00003': 2, 'C00001': 2}),
('ATP + D-arbino-hexulose <=> ADP + D-Fructose-1-phophte', # with some typos
{'C00002': -1, 'C00095': -1, 'C00008': 1, 'C01094': 1}),
]
rm = ReactionMatcher()
for plaintext, kegg_id_to_coeff in formulas:
rxn = rm.match(plaintext)
if rxn is None:
self.fail('unable to parse the plaintext formula\n' + plaintext)
else:
self.assertDictEqual(rxn.kegg_id_to_coeff, kegg_id_to_coeff)
if __name__ == '__main__':
unittest.main()
[build-system]
requires = [
"setuptools>=30.3.0",
"wheel"
]
numpy>=1.15.2
scipy>=1.1.0
optlang>=1.4.3
pandas>=0.23.4
nltk>=3.2.5
pyparsing>=2.2.0
sbtab>=0.9.62
matplotlib>=3.0.0
[metadata]
name = equilibrator-api
url = https://gitlab.com/elad.noor/equilibrator-api
author = Elad Noor
author_email = [email protected]
maintainer = Elad Noor
maintainer_email = [email protected]
classifiers =
Development Status :: 4 - Beta
Intended Audience :: Science/Research
Topic :: Scientific/Engineering :: Chemistry
License :: OSI Approved :: MIT License
Natural Language :: English
Programming Language :: Python :: 3
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
license = MIT License
description = Standard reaction Gibbs energy estimation for biochemical reactions.
long_description = file: README.rst
keywords =
Gibbs energy
biochemical reaction
equilibrator
[options]
zip_safe = True
install_requires =
importlib_resources
numpy>=1.15.2
scipy>=1.1.0
optlang>=1.4.3
pandas>=0.23.4
nltk>=3.2.5
pyparsing>=2.2.0
sbtab>=0.9.49
matplotlib>=3.0.0
requests
python_requires = >=3.5
tests_require =
tox
packages = find:
package_dir =
= src
[options.packages.find]
where = src
[options.package_data]
equilibrator_api.data =
*.csv
*.tsv
*.json
*.npz
[wheel]
universal = 1
[bdist_wheel]
universal = 1
[flake8]
max-line-length = 80
exclude =
__init__.py
docs
[pydocstyle]
match_dir = equilibrator_api
[tool:unittest]
testpaths = tests
[isort]
line_length = 80
indent = 4
multi_line_output = 4
lines_after_imports = 2
known_first_party = equilibrator_api
known_third_party =
numpy
pandas
pybel
unittest
scipy
optlang
nltk
pyparsing
sbtab
matplotlib
# See the docstring in versioneer.py for instructions. Note that you must
# re-run 'versioneer.py setup' after changing this section, and commit the
# resulting files.
[versioneer]
VCS = git
style = pep440
versionfile_source = src/equilibrator_api/_version.py
versionfile_build = equilibrator_api/_version.py
tag_prefix =
parentdir_prefix = equilibrator_api-
#!/usr/bin/python3
#!/usr/bin/env python
__doc__ = "eQuilibrator API - A command-line API with minimal dependencies for calculation of standard thermodynamic potentials of biochemical reactions using the data found on eQuilibrator. Does not require any network connections."
__version__ = '0.1.8'
import os
import versioneer
from setuptools import setup
try:
import setuptools
except Exception as ex:
print(ex)
os.sys.exit(-1)
mydata_files = ['cc_compounds.json',
'cc_preprocess.npz',
'cofactors.csv',
'iJO1366_reactions.csv',
'kegg_compound_names.tsv',
'kegg_compound_renaming.tsv']
data_files = [('data',
[os.path.join('equilibrator_api/data', f) for f in mydata_files])]
# Most arguments are set in the `setup.cfg`.
setup(
version=versioneer.get_version(),
cmdclass=versioneer.get_cmdclass(),
)
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name='equilibrator_api',
version=__version__,
description=__doc__,
long_description=long_description,
url='https://gitlab.com/elad.noor/equilibrator-api',
author='Elad Noor',
author_email='[email protected]',
license='MIT',
packages=['equilibrator_api'],
install_requires=[
'numpy>=1.15.2',
'scipy>=1.1.0',
'optlang>=1.4.3',
'pandas>=0.23.4',
'nltk>=3.2.5',
'pyparsing>=2.2.0',
'sbtab>=0.9.49',
'matplotlib>=3.0.0',
],
include_package_data=True,
data_files = data_files
)
# This file helps to compute a version number in source trees obtained from
# git-archive tarball (such as those provided by githubs download-from-tag
# feature). Distribution tarballs (built by setup.py sdist) and build
# directories (produced by setup.py build) will contain a much shorter file
# that just contains the computed version number.
# This file is released into the public domain. Generated by
# versioneer-0.18 (https://github.com/warner/python-versioneer)
"""Git implementation of _version.py."""
import errno
import os
import re
import subprocess
import sys
def get_keywords():
"""Get the keywords needed to look up the version information."""
# these strings will be replaced by git during git-archive.
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
git_refnames = "$Format:%d$"
git_full = "$Format:%H$"
git_date = "$Format:%ci$"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
class VersioneerConfig:
"""Container for Versioneer configuration parameters."""
def get_config():
"""Create, populate and return the VersioneerConfig() object."""
# these strings are filled in when 'setup.py versioneer' creates
# _version.py
cfg = VersioneerConfig()
cfg.VCS = "git"
cfg.style = "pep440"
cfg.tag_prefix = ""
cfg.parentdir_prefix = "equilibrator_api-"
cfg.versionfile_source = "equilibrator_api/_version.py"
cfg.verbose = False
return cfg
class NotThisMethod(Exception):
"""Exception raised if a method is not valid for the current scenario."""
LONG_VERSION_PY = {}
HANDLERS = {}
def register_vcs_handler(vcs, method): # decorator
"""Decorator to mark a method as the handler for a particular VCS."""
def decorate(f):
"""Store f in HANDLERS[vcs][method]."""
if vcs not in HANDLERS:
HANDLERS[vcs] = {}
HANDLERS[vcs][method] = f
return f
return decorate
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
env=None):
"""Call the given command(s)."""
assert isinstance(commands, list)
p = None
for c in commands:
try:
dispcmd = str([c] + args)
# remember shell=False, so use git.cmd on windows, not just git
p = subprocess.Popen([c] + args, cwd=cwd, env=env,
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr
else None))
break
except EnvironmentError:
e = sys.exc_info()[1]
if e.errno == errno.ENOENT:
continue
if verbose:
print("unable to run %s" % dispcmd)
print(e)
return None, None
else:
if verbose:
print("unable to find command, tried %s" % (commands,))
return None, None
stdout = p.communicate()[0].strip()
if sys.version_info[0] >= 3:
stdout = stdout.decode()
if p.returncode != 0:
if verbose:
print("unable to run %s (error)" % dispcmd)
print("stdout was %s" % stdout)
return None, p.returncode
return stdout, p.returncode
def versions_from_parentdir(parentdir_prefix, root, verbose):
"""Try to determine the version from the parent directory name.
Source tarballs conventionally unpack into a directory that includes both
the project name and a version string. We will also support searching up
two directory levels for an appropriately named parent directory
"""
rootdirs = []
for i in range(3):
dirname = os.path.basename(root)
if dirname.startswith(parentdir_prefix):
return {"version": dirname[len(parentdir_prefix):],
"full-revisionid": None,
"dirty": False, "error": None, "date": None}
else:
rootdirs.append(root)
root = os.path.dirname(root) # up a level
if verbose:
print("Tried directories %s but none started with prefix %s" %
(str(rootdirs), parentdir_prefix))
raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
@register_vcs_handler("git", "get_keywords")
def git_get_keywords(versionfile_abs):
"""Extract version information from the given file."""
# the code embedded in _version.py can just fetch the value of these
# keywords. When used from setup.py, we don't want to import _version.py,
# so we do it with a regexp instead. This function is not used from
# _version.py.
keywords = {}
try:
f = open(versionfile_abs, "r")
for line in f.readlines():
if line.strip().startswith("git_refnames ="):
mo = re.search(r'=\s*"(.*)"', line)
if mo:
keywords["refnames"] = mo.group(1)
if line.strip().startswith("git_full ="):
mo = re.search(r'=\s*"(.*)"', line)
if mo:
keywords["full"] = mo.group(1)
if line.strip().startswith("git_date ="):
mo = re.search(r'=\s*"(.*)"', line)
if mo:
keywords["date"] = mo.group(1)
f.close()
except EnvironmentError:
pass
return keywords
@register_vcs_handler("git", "keywords")
def git_versions_from_keywords(keywords, tag_prefix, verbose):
"""Get version information from git keywords."""
if not keywords:
raise NotThisMethod("no keywords at all, weird")
date = keywords.get("date")
if date is not None:
# git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant
# datestamp. However we prefer "%ci" (which expands to an "ISO-8601
# -like" string, which we must then edit to make compliant), because