pyMBE-dev / pyMBE

pyMBE provides tools to facilitate building up molecules with complex architectures in the Molecular Dynamics software ESPResSo. For an up-to-date API documention please check our website:
https://pymbe-dev.github.io/pyMBE/pyMBE.html
GNU General Public License v3.0
6 stars 8 forks source link

Convert pyMBE into a package #13

Open jngrad opened 6 months ago

jngrad commented 6 months ago

The pyMBE module is currently not constructed as a package, thus limiting the ways in which the Python standard library can interact with it. For example, locating text file resources such as pKa datasets required the introduction of the class method pymbe_library.get_resource(), which takes a relative path as argument and joins it to the __file__ path to create an absolute path. Making this method an internal method would force all pyMBE method to be only able to read datasets stored in the pyMBE source tree, which would force users to copy their custom datasets in the source tree directly.

To make pyMBE behave like a package, we could adapt its structure to look more like this:

./
 |---> pyMBE/
 |       |---> pyMBE.py
 |       |---> __init__.py
 |       |---> reference_parameters/
 |       |---> handy_scripts/
 |---> testsuite/
 |---> samples/

The __init__.py file would contain this code:

from .pyMBE import *

The pyMBE.py file would be able to do relative imports of the form:

from .handy_scripts.handy_functions import setup_electrostatic_interactions

Datasets can then be found using the importlib.resources mechanism, which replaces the deprecated pkg_resources tool. Simulation scripts would work like this:

import pyMBE
import importlib.resources
pmb = pyMBE.pymbe_library()
pmb.load_pka_set(filename=importlib.resources.files(pyMBE) / "reference_parameters" / "pka_sets" / "CRC1991.txt")

Notice how we no longer provide paths as strings containing the '/' character, but instead use the / operator which joins the path components using an OS-specific separator character ('/' on UNIX and '\' on Windows). You can try this out now with the following code:

$ python3 -c 'import pyMBE,importlib.resources as ir;print(ir.files(pyMBE)/"pka"/"CRC1991.txt")'
TypeError: <module 'pyMBE' from '/work/jgrad/pyMBE/pyMBE.py'> is not a package
$ echo "from .pyMBE import *" > __init__.py
$ cd .. # going up so Python can detect the "pyMBE" folder as a package and import its __init__.py
$ python3 -c 'import pyMBE,importlib.resources as ir;print(ir.files(pyMBE)/"pka"/"CRC1991.txt")'
/work/jgrad/pyMBE/pka/CRC1991.txt
$ python3 -c 'import pyMBE;print(pyMBE.pymbe_library)'
<class 'pyMBE.pyMBE.pymbe_library'>
$ cd pyMBE
$ rm __init__.py

The tree structure outlined on top also makes it clear what belongs to the core package (pyMBE.py, handy scripts, datasets) and what are auxiliary files (testsuite, samples). The latter aren't typically deployed during packaging. The new tree structure would not break scripts that have been modernized to run in the pyMBE virtual environment. The pymbe.pth file of the pyMBE virtual environment would need to be updated by adding pyMBE/ in the absolute path on the first line.