Skip to content

Commit

Permalink
Merge pull request #284 from Becksteinlab/update-resource-management
Browse files Browse the repository at this point in the history
- use importlib.resources instead of pkg_resources
- fix/ignore any remaining warnings
  • Loading branch information
orbeckst authored Jun 17, 2024
2 parents 4574592 + 51a1e79 commit a5bcc8a
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 46 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
==============================

2024-??-?? 0.9.1
orbeckst

* templates in `config.templates` are now stored as
`importlib.resources.abc.Traversable` (PosixPath-like) objects instead of
strings of paths to files in the file system because we switched from
pkg_resources to importlib.resources for managing access to included files
(#282)
* fixed reporting of failures of GromacsCommand and DeprecationWarnings for use
of \s in regular expression strings (#285)

Expand Down
45 changes: 24 additions & 21 deletions gromacs/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,7 @@
import sys

from configparser import ConfigParser

from pkg_resources import resource_filename, resource_listdir
from importlib import resources

from . import utilities

Expand Down Expand Up @@ -307,37 +306,40 @@


def _generate_template_dict(dirname):
"""Generate a list of included files *and* extract them to a temp space.
"""Generate a list of included files *and* make them available.
Template files are stored inside the package. They are made available as
:class:` importlib.resources.abc.Traversable` object with
:mod:`pathlib.Path`-like methods. They can normally be read with the usual
:func:`open` function but if a real file needs to be present, use
:func:`importlib.resources.as_file`.
All template entries are stored in :data:`config.templates`.
.. SeeAlso:: importlib.resources
Templates have to be extracted from the egg because they are used
by external code. All template filenames are stored in
:data:`config.templates`.
"""
# for Python >= 3.12:
# replace `sys.modules[__name__].__package__` with `__name__`
return dict(
(resource_basename(fn), resource_filename(__name__, dirname + "/" + fn))
for fn in resource_listdir(__name__, dirname)
if not fn.endswith("~")
(entry.name, entry)
for entry in resources.files(sys.modules[__name__].__package__)
.joinpath(dirname)
.iterdir()
if not entry.name.endswith(("~", "__pycache__", "__init__.py"))
)


def resource_basename(resource):
"""Last component of a resource (which always uses '/' as sep)."""
if resource.endswith("/"):
resource = resource[:-1]
parts = resource.split("/")
return parts[-1]


templates = _generate_template_dict("templates")
"""*GromacsWrapper* comes with a number of templates for run input files
and queuing system scripts. They are provided as a convenience and
examples but **WITHOUT ANY GUARANTEE FOR CORRECTNESS OR SUITABILITY FOR
ANY PURPOSE**.
All template filenames are stored in
:data:`gromacs.config.templates`. Templates have to be extracted from
the GromacsWrapper python egg file because they are used by external
code: find the actual file locations from this variable.
All templates are stored in :data:`gromacs.config.templates` as :class:`
importlib.resources.abc.Traversable` objects with :mod:`pathlib.Path`-like
methods. They can normally be read with the usual :func:`open` function but if
a real file needs to be present, use :func:`importlib.resources.as_file`.
**Gromacs mdp templates**
Expand All @@ -357,6 +359,7 @@ def resource_basename(resource):
The queing system scripts are highly specific and you will need to add your
own into :data:`gromacs.config.qscriptdir`.
See :mod:`gromacs.qsub` for the format and how these files are processed.
"""

#: The default template for SGE/PBS run scripts.
Expand Down
2 changes: 1 addition & 1 deletion gromacs/fileformats/xvg.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ def set(self, a):

def _get_colors(self, color, columns):
try:
cmap = matplotlib.cm.get_cmap(color)
cmap = matplotlib.colormaps[color]
colors = cmap(
matplotlib.colors.Normalize()(
numpy.arange(len(columns[1:]), dtype=float)
Expand Down
2 changes: 1 addition & 1 deletion gromacs/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ class Release(object):
"""

gromacs_version = re.compile(
r"^G[rR][oO][mM][aA][cC][sS] version:" "\s*(VERSION)?\s*(?P<version>.+)$"
r"^G[rR][oO][mM][aA][cC][sS] version:\s*(VERSION)?\s*(?P<version>.+)$"
)

def __init__(self):
Expand Down
11 changes: 9 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ rmprefix = "release-"
file = "gromacs/_version.py"



[tool.black]
extend-exclude = "tests/(test_core|fileformats/test_convert)\\.py"
extend-exclude = "tests/(test_core|fileformats/test_convert)\\.py"


[tool.pytest.ini_options]
filterwarnings = [
"ignore::gromacs.AutoCorrectionWarning",
"ignore::gromacs.LowAccuracyWarning",
"ignore::numkit.LowAccuracyWarning",
]
9 changes: 7 additions & 2 deletions tests/datafiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
"""


import sys
import os.path
from pkg_resources import resource_filename
from importlib import resources


def datafile(name):
return resource_filename(__name__, os.path.join("data", name))
return (
resources.files(sys.modules[__name__].__package__)
.joinpath("data")
.joinpath(name)
)
46 changes: 27 additions & 19 deletions tests/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,17 @@ def energy_minimize(solvate, low_performance):
TMPDIR, solvate_args = solvate
nt = 2 if low_performance else 0
with TMPDIR.as_cwd():
em_args = gromacs.setup.energy_minimize(
mdrun_args={"nt": nt},
integrator="steep",
emtol=5000,
maxwarn=1,
**solvate_args,
)
with pytest.warns(
gromacs.UsageWarning,
match="Unprocessed mdp option are interpreted as options for grompp",
):
em_args = gromacs.setup.energy_minimize(
mdrun_args={"nt": nt},
integrator="steep",
emtol=5000,
maxwarn=1,
**solvate_args,
)
return TMPDIR, em_args


Expand Down Expand Up @@ -106,18 +110,22 @@ def test_energy_minimize_custom_mdp(
TMPDIR, solvate_args = solvate
nt = 2 if low_performance else 0
with TMPDIR.as_cwd():
try:
em_args = gromacs.setup.energy_minimize(
mdrun_args={"nt": nt}, mdp=mdp, emtol=5000, **solvate_args
)
except gromacs.exceptions.GromacsError as err:
# sometimes the em does not converge at all, e.g. 5.02988e+04 on atom 3277;
# (happens on Travis Linux with Gromacs 4.6.5 but not locally or on Travis OSX) so we
# re-run with a ridiculous tolerance so that we can at least test that the whole
# function can run to completion
em_args = gromacs.setup.energy_minimize(
mdrun_args={"nt": nt}, mdp=mdp, emtol=6e4, **solvate_args
)
with pytest.warns(
gromacs.UsageWarning,
match="Unprocessed mdp option are interpreted as options for grompp",
):
try:
em_args = gromacs.setup.energy_minimize(
mdrun_args={"nt": nt}, mdp=mdp, emtol=5000, **solvate_args
)
except gromacs.exceptions.GromacsError as err:
# sometimes the em does not converge at all, e.g. 5.02988e+04 on atom 3277;
# (happens on Travis Linux with Gromacs 4.6.5 but not locally or on Travis OSX) so we
# re-run with a ridiculous tolerance so that we can at least test that the whole
# function can run to completion
em_args = gromacs.setup.energy_minimize(
mdrun_args={"nt": nt}, mdp=mdp, emtol=6e4, **solvate_args
)
assert os.path.exists(em_args["struct"])
assert os.path.exists(em_args["top"])
assert em_args["mainselection"] == '"Protein"'
Expand Down

0 comments on commit a5bcc8a

Please sign in to comment.