Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial attempt at a plugin detector. #292

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions foyer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from foyer.forcefield import Forcefield
from foyer.forcefields import forcefields
from foyer.version import version
from foyer.plugins import collect_plugins
41 changes: 41 additions & 0 deletions foyer/plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import foyer
import glob
import os

def collect_plugins(plugin_names=None):
"""
Detects which plugins are installed

Arguments
---------
plugin_names: list, default=None
A list of non-standard plugin names (strings) to additionally
check for.
"""

plugin_funcs = [func for func in dir(foyer.forcefields)
if "load" in func and "__" not in func]

if plugin_names:
plugin_funcs += plugin_names

plugins = dict()
for plugin_func in plugin_funcs:
plugin_loader_func = eval("foyer.forcefields.{}".format(plugin_func))
# Assumes all plugins are named "load_{plugin_name}"
plugin_name = "_".join(plugin_func.split("_")[1:])
# TODO: plugin_version = get_version_info
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any initial thoughts on how we should store this information? I see we want it to be accessible from the loader but I'm not sure how to go about doing that - maybe these functions should be expanded into (lightweight) classes?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out my plugins branch for the PFE repo. To demonstrate how FF versioning would work, I added some dummy forcefield versions and file organization for adding forcefields versions. Version and xml_path are returned when the forcefield is loaded, and can be easily accessed by the plugin detector in foyer:

>>> import foyer
>>> pfe = foyer.forcefields.load_PFE()
>>> pfe.version
'1.0.0'
>>> pfe.xml_path
['/Users/parashara/Documents/devel/git/forcefield_perfluoroethers/perfluoroethers/xml/current/PFE.xml']
>>> pfe = foyer.forcefields.load_PFE("0.0.2")
>>> pfe.version
'0.0.2'
>>> pfe.xml_path
['/Users/parashara/Documents/devel/git/forcefield_perfluoroethers/perfluoroethers/xml/versions/0.0.2/PFE.xml']

plugin_dir = eval("foyer.forcefields.{}.__globals__['__file__']".format(plugin_func))
plugin_dir = os.path.dirname(plugin_dir)
# TODO: plugin_xml_path = get_xml_path
# This assumes that plugin directory tree is consistent.
# Does not consider versioned FFs.
plugin_xml_path = glob.glob("{}/xml/*xml".format(plugin_dir))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to semi-intelligently glob recursively, i.e. to get both '{}/xml/*xml' and '{}/*xml'? We probably don't need arbitrary recursion, maybe handling 2-3 cases is sufficient.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me this is what I get:

(Pdb) plugin_dir
'/Users/raymatsumoto/science/forcefields/forcefield_perfluoroethers/perfluoroethers/perfluoroethers.py'
(Pdb) 

So I'm unable to glob this path.


plugin = {plugin_name : {"version" : None,
"load_function" : plugin_loader_func,
"xml_path" : plugin_xml_path
}}
plugins.update(plugin)

return plugins