Skip to content

Commit

Permalink
add example doc on method get_user_collection(), black all code
Browse files Browse the repository at this point in the history
and fix warning in docstring during sphinx build.
  • Loading branch information
shahzebsiddiqui committed Mar 25, 2020
1 parent 704d12d commit 4f8c4e5
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 42 deletions.
5 changes: 3 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
#
import os
import sys
sys.path.insert(0, os.path.abspath('..'))

sys.path.insert(0, os.path.abspath(".."))

from lmod import LMODULE_VERSION
from sphinx.ext.apidoc import main as sphinx_apidoc
Expand All @@ -25,7 +26,7 @@

# The full version, including alpha/beta/rc tags
version = LMODULE_VERSION
release = "beta"
release = "alpha"


# -- General configuration ---------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Welcome to lmodule documentation!
`lmodule <https://github.com/HPC-buildtest/lmodule>`_ is a Python API for `Lmod <https://lmod.readthedocs.io/en/latest>`_
module system. lmodule was originally part of `buildtest <https://github.com/HPC-buildtest/buildtest-framework>`_ and
we decided that it could benefit the entire community for folks interested in using the API but not relying on buildtest.
The documentation is built for version |version| on |today|

What is lmodule?
-----------------
Expand Down
73 changes: 73 additions & 0 deletions docs/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,76 @@ You can get the Lmod version by using the ``version()`` method.
>>> a.version()
'7.8.16'
Retrieve User Collections
--------------------------

Lmod user collection are typically found in **$HOME/.lmod.d** and you can get all collections by running ``module -t savelist``.

Similarly, we have a method ``get_user_collections`` that can return a **list** of user collections as shown below

.. code-block:: python
>>> from lmod.module import get_user_collections
>>> get_user_collections()
['GCC', 'Python', 'default', 'gcc_zlib', 'settarg', 'zlib']
This could be used in conjunction with ``Module`` class with options like ``get_collection``, ``test_collection``, ``describe``
to perform operation on the user collections.

Shown below is an example of showing all user collections in a simple for-loop using the ``describe`` method from Module class

.. code-block:: python
>>> for collection in get_user_collections():
... Module().describe(collection)
...
Collection "GCC" contains:
1) GCCcore/8.3.0
Collection "Python" contains:
1) GCCcore/8.3.0 7) SQLite/3.29.0-GCCcore-8.3.0
2) bzip2/1.0.8-GCCcore-8.3.0 8) XZ/5.2.4-GCCcore-8.3.0
3) zlib/1.2.11-GCCcore-8.3.0 9) GMP/6.1.2-GCCcore-8.3.0
4) ncurses/6.1-GCCcore-8.3.0 10) libffi/3.2.1-GCCcore-8.3.0
5) libreadline/8.0-GCCcore-8.3.0 11) Python
6) Tcl/8.6.9-GCCcore-8.3.0
Collection "default" contains:
1) settarg
Collection "gcc_zlib" contains:
1) GCCcore/8.3.0 2) zlib
Collection "settarg" contains:
1) settarg
Collection "zlib" contains:
1) zlib
Likewise, we can easily test all user collection using ``test_collection`` which gives opportunity to ensure all your
user collection are valid before using them in your script

.. code-block:: python
>>> for collection in get_user_collections():
... Module(debug=True).test_collection(collection)
...
[DEBUG] Executing command: module restore GCC
[DEBUG] Return Code: 0
0
[DEBUG] Executing command: module restore Python
[DEBUG] Return Code: 0
0
[DEBUG] Executing command: module restore default
[DEBUG] Return Code: 0
0
[DEBUG] Executing command: module restore gcc_zlib
[DEBUG] Return Code: 0
0
[DEBUG] Executing command: module restore settarg
[DEBUG] Return Code: 0
0
[DEBUG] Executing command: module restore zlib
[DEBUG] Return Code: 0
0
2 changes: 1 addition & 1 deletion examples/moduleloadtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@
"/usr/share/lmod/lmod/modulefiles/Core", include=["settarg"], exclude=["lmod"]
)
k = ModuleLoadTest("/mxg-hpc/users/ssi29/spack/modules/linux-rhel7-x86_64/Core")
l = ModuleLoadTest("/usr/share/lmod/lmod/modulefiles/Core", debug=True,login=True)
l = ModuleLoadTest("/usr/share/lmod/lmod/modulefiles/Core", debug=True, login=True)
32 changes: 16 additions & 16 deletions lmod/module.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import subprocess


def get_user_collections():
"""Get all user collections that is retrieved by running ``module -t savelist``. The output
is of type ``list`` and each entry in list is the name of the user collection.
Expand All @@ -27,7 +28,7 @@ class Module:
provided by Lmod
Public Methods:
---------------
avail: implements ``module avail`` option
version: gets Lmod version by reading LMOD_VERSION
is_avail: implements ``module is-avail`` option
Expand All @@ -46,15 +47,14 @@ def __init__(self, modules=None, purge=True, force=False, debug=False):
can be useful for troubleshooting.
Parameters:
-----------
:param modules: list of modules
:param purge: boolean to control whether to purge modules before loading
:param force: boolean to control whether to force purge modules before loading
:param debug: debug mode for troubleshooting
:param modules: list of modules
:type modules: list, optional
:param purge: boolean to control whether to purge modules before loading
:type purge: bool, optional
:param force: boolean to control whether to force purge modules before loading
:type force: bool, optional
:param debug: debug mode for troubleshooting
:type debug: bool, optional
"""

Expand Down Expand Up @@ -91,13 +91,13 @@ def __init__(self, modules=None, purge=True, force=False, debug=False):
else:
self.module_load_cmd = ["module purge &&"] + self.module_load_cmd

def avail(self,name=None):
def avail(self, name=None):
"""This method implements the ``module avail`` command. The output of module avail will return available
modules in the system. The output is returned as a list using the ``module -t avail`` which presents the
output in a single line per module.
Parameters:
-----------
:param name: argument passed to ``module avail``. This is used for showing what modules are available
:type name: str, optional
Expand Down Expand Up @@ -134,7 +134,7 @@ def is_avail(self, name):
before loading it. The return value is a 0 or 1.
Parameters:
------------
:param name: argument passed to ``module is-avail``. This is used for checking if module is available
:type name: str, required
Expand Down Expand Up @@ -169,7 +169,7 @@ def test_modules(self, login=False):
specified. The return value is a return code (type ``int``) of the ``module load`` command.
Parameters:
-----------
:param login: When ``login=True`` is set, it will run the test in a login shell, the default is to run in a sub-shell
:type login: bool, optional
Expand All @@ -181,7 +181,7 @@ def test_modules(self, login=False):

# run test in login shell
if login:
cmd_executed = "bash -l -c \"" + cmd_executed + "\""
cmd_executed = 'bash -l -c "' + cmd_executed + '"'

ret = subprocess.run(
cmd_executed,
Expand All @@ -205,8 +205,8 @@ def save(self, collection="default"):
we are running ``module save <collection>``. The collection name must be of type ``str`` in order for
this to work, otherwise an exception of ``TypeError`` will be raised.
Paramters:
-----------
Parameters:
:param collection: collection name to save modules. If none specified, ``default`` is the collection.
:type collection: str, optional
"""
Expand Down Expand Up @@ -241,7 +241,7 @@ def describe(self, collection="default"):
If collection name is not of type ``str``, then an exception of ``TypeError`` will be raised.
Parameters:
-----------
:param collection: name of user collection to show.
:type collection: str, optional
"""
Expand Down Expand Up @@ -274,7 +274,7 @@ def get_collection(self, collection="default"):
``TypeError`` will be raised.
Parameters:
-----------
:param collection: collection name to restore
:type collection: str, optional
Expand All @@ -296,7 +296,7 @@ def test_collection(self, collection="default"):
If argument to method is not of type ``str`` an exception of ``TypeError`` will be raised.
Parameters:
-----------
:param collection: collection name to test
:type collection: str, optional
Expand Down
20 changes: 15 additions & 5 deletions lmod/moduleloadtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from lmod.module import Module
from lmod.spider import Spider


class ModuleLoadTest:
"""This is the class declaration of ModuleLoadTest. This class will automate module load
test for all modules in one or more module tree (Software Stack) retrieved by Spider class. The output
Expand Down Expand Up @@ -68,7 +69,10 @@ def __init__(
filter_modules = None

spider_cmd = Spider(self.tree)
module_dict, modules = spider_cmd.get_modules(self.name), list(spider_cmd.get_modules(self.name).values())
module_dict, modules = (
spider_cmd.get_modules(self.name),
list(spider_cmd.get_modules(self.name).values()),
)

if self.include:
filter_modules = set(self.include).intersection(modules)
Expand All @@ -86,19 +90,25 @@ def __init__(

for module_name in modules:
module_cmd = Module(
module_name, purge=self.purge, force=self.force, debug=self.debug,
module_name, purge=self.purge, force=self.force, debug=self.debug,
)
ret = module_cmd.test_modules(self.login)

# extract modulefile based on module name. This is basically getting the key from dictionary (module_dict)
# This is only used for printing purposes since it helps to know which module is tested. Simply putting
# the full module canonical name is not enough.
modulefile = list(module_dict.keys())[list(module_dict.values()).index(module_name)]
modulefile = list(module_dict.keys())[
list(module_dict.values()).index(module_name)
]

if ret == 0:
print(f"PASSED - Module Name: {module_name} ( modulefile={modulefile} )")
print(
f"PASSED - Module Name: {module_name} ( modulefile={modulefile} )"
)
else:
print(f"FAILED - Module Name: {module_name} ( modulefile={modulefile} )")
print(
f"FAILED - Module Name: {module_name} ( modulefile={modulefile} )"
)

modulecount += 1

Expand Down
22 changes: 13 additions & 9 deletions lmod/spider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import os
import subprocess


class Spider:
"""This is the class declaration of Spider class which emulates the spider tool provided by Lmod. The spider command
is typically used to build the spider cache in your site. We use the spider tool to fetch all module files.
Public Methods:
---------------
get_trees: returns all module trees used with spider command
get_names: returns all top-level keys from spider which is the software name
get_modules: returns all full canonical module name.
Expand All @@ -23,7 +24,7 @@ def __init__(self, tree=None):
Parameters:
------------
:param tree: User can specify one or more module trees to query from spider. Trees must be separated by colon (``:``)
:type tree: str, optional
"""
Expand All @@ -49,8 +50,8 @@ def get_names(self, name=[]):
"""Returns a list of software names which are found by returning the top-level key from json structure.
One can specify a list of module names to filter output.
Parameters
-----------
Parameters:
:param name: a list of software name to filter output
:type name: list, optional
Expand All @@ -72,7 +73,6 @@ def get_modules(self, name=[]):
modules.
Parameters:
------------
:param name: a list of software name to filter output
:type name: type, required
Expand All @@ -86,11 +86,15 @@ def get_modules(self, name=[]):
for module in self.get_names(name):
for mpath in self.spider_content[module].keys():
# skip modules that start with .version and .modulerc since they are not modules.
module_version = os.path.basename(self.spider_content[module][mpath]["fullName"])
if module_version.startswith(".version") or module_version.startswith(".modulerc"):
module_version = os.path.basename(
self.spider_content[module][mpath]["fullName"]
)
if module_version.startswith(".version") or module_version.startswith(
".modulerc"
):
continue

module_names[mpath]=self.spider_content[module][mpath]["fullName"]
module_names[mpath] = self.spider_content[module][mpath]["fullName"]

return module_names

Expand Down Expand Up @@ -120,7 +124,7 @@ def get_all_versions(self, key):
in spider output.
Parameters:
-----------
:param key: name of software
:type key: str, required
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
long_description=open("README.rst").read(),
url="https://github.com/HPC-buildtest/lmodule",
license="MIT",
python_requires='~=3.6',
python_requires="~=3.6",
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX :: Linux",
Expand Down
15 changes: 11 additions & 4 deletions tests/test_moduleloadtest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
from lmod.moduleloadtest import ModuleLoadTest


class TestModuleLoadTest:
def test_disable_purge(self):
ModuleLoadTest(purge=False)
Expand All @@ -15,10 +16,10 @@ def test_filter_exclude(self):
ModuleLoadTest(exclude=["lmod"])

def test_filter_include_exclude(self):
ModuleLoadTest(include=["lmod"],exclude=["lmod"])
ModuleLoadTest(include=["lmod"], exclude=["lmod"])

def test_filter_name(self):
ModuleLoadTest(name=["lmod","settarg"])
ModuleLoadTest(name=["lmod", "settarg"])

def test_by_count(self):
ModuleLoadTest(count=1)
Expand All @@ -27,7 +28,13 @@ def test_debug(self):
ModuleLoadTest(debug=True)

def test_lmod_tree(self):
ModuleLoadTest(os.path.join(os.getenv("LMOD_PKG"),"modulefiles/Core"),debug=True)
ModuleLoadTest(
os.path.join(os.getenv("LMOD_PKG"), "modulefiles/Core"), debug=True
)

def test_login_test(self):
ModuleLoadTest(os.path.join(os.getenv("LMOD_PKG"),"modulefiles/Core"), debug=True, login=True)
ModuleLoadTest(
os.path.join(os.getenv("LMOD_PKG"), "modulefiles/Core"),
debug=True,
login=True,
)
Loading

0 comments on commit 4f8c4e5

Please sign in to comment.