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

Black integration #17

Merged
merged 3 commits into from Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 8 additions & 18 deletions .github/workflows/black.yml
Expand Up @@ -13,24 +13,14 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Setup black linter
run: conda create --quiet --name black black
# use black github action see https://black.readthedocs.io/en/stable/integrations/github_actions.html
- uses: psf/black@stable
with:
options: "--check"
src: "lmod examples"

- name: Lint python code
- name: Check imports with pyflakes
run: |
export PATH="/usr/share/miniconda/bin:$PATH"
source activate black
black --check lmod tests/ examples/

- name: Derive organization name
if: github.event_name == 'pull_request' && failure()
run: echo ::set-env name=IS_FORKED::$(if [ -z "$GITHUB_HEAD_REF" ]; then echo "no"; else echo "yes"; fi)
shell: bash

- name: Comment Pull Request
if: github.event_name == 'pull_request' && failure() && env.IS_FORKED == 'no'
uses: marocchino/sticky-pull-request-comment@v1.1.0
with:
message: 'Please format your code with [black](https://black.readthedocs.io) version 19.3b0: `black lmod tests/ examples/`.'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
pip install pyflakes
pyflakes lmod

6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 21.9b0
hooks:
- id: black
language_version: python3.7
2 changes: 1 addition & 1 deletion docs/conf.py
Expand Up @@ -71,4 +71,4 @@

intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
}
}
2 changes: 2 additions & 0 deletions docs/requirements.txt
@@ -1,6 +1,8 @@
black
codecov
coverage
docutils==0.16
pre-commit
pytest
Sphinx
sphinx-rtd-theme
Expand Down
26 changes: 10 additions & 16 deletions examples/environmentmodules_moduleloadtest.py
Expand Up @@ -18,29 +18,23 @@
if os.path.exists(tree):
print(f"Skipping tree: {tree}")
continue
if re.search("(\(default\))$",module):
module = module.replace('(default)','')
if re.search("(\(default\))$", module):
module = module.replace("(default)", "")

cmd = Module(module,debug=True)
cmd = Module(module, debug=True)
ret = cmd.test_modules(login=True)
total += 1
# if returncode is 0 mark as PASS
if ret == 0:
pass_counter+=1
pass_counter += 1
else:
fail_counter+=1
fail_counter += 1

pass_rate = pass_counter * 100 / total
fail_rate = fail_counter * 100 / total

print ("-------- SUMMARY ---------")
print (f"Total Pass: {pass_counter}/{total}")
print (f"Total Failure: {fail_counter}/{total}")
print (f"PASS RATE: {pass_rate:.3f}")
print (f"FAIL RATE: {fail_rate:.3f}")






print("-------- SUMMARY ---------")
print(f"Total Pass: {pass_counter}/{total}")
print(f"Total Failure: {fail_counter}/{total}")
print(f"PASS RATE: {pass_rate:.3f}")
print(f"FAIL RATE: {fail_rate:.3f}")
175 changes: 87 additions & 88 deletions lmod/module.py
Expand Up @@ -4,11 +4,11 @@

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.
is of type ``list`` and each entry in list is the name of the user collection.

:return: Return all user collections
:rtype: list
"""
:return: Return all user collections
:rtype: list
"""

collections = "module -t savelist"
ret = subprocess.run(
Expand All @@ -25,37 +25,37 @@ def get_user_collections():

class Module:
"""This is the class declaration of Module class which emulates the ``module`` command
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
get_command: gets the ``module load`` command based on modules passed to the class
test_modules: test the ``module load`` command based on modules passed to the class
save: implements ``module save`` option
describe: implements ``module describe`` option
get_collection: gets the ``module restore`` command with the specified collection name
test_collection: test the ``module restore`` command with the specified collection name
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
get_command: gets the ``module load`` command based on modules passed to the class
test_modules: test the ``module load`` command based on modules passed to the class
save: implements ``module save`` option
describe: implements ``module describe`` option
get_collection: gets the ``module restore`` command with the specified collection name
test_collection: test the ``module restore`` command with the specified collection name
"""

def __init__(self, modules=None, purge=True, force=False, debug=False):
"""Initialize method for Module class. This class can accept module names that
can be used to load or test modules. You can tweak the behavior of how module
command is generated such as purging of force purge modules. The debug option
can be useful for troubleshooting.

Parameters:

: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
can be used to load or test modules. You can tweak the behavior of how module
command is generated such as purging of force purge modules. The debug option
can be useful for troubleshooting.

Parameters:

: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
"""

self.debug = debug
Expand Down Expand Up @@ -93,16 +93,16 @@ def __init__(self, modules=None, purge=True, force=False, debug=False):

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.
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:
Parameters:

:param name: argument passed to ``module avail``. This is used for showing what modules are available
:type name: str, optional
:param name: argument passed to ``module avail``. This is used for showing what modules are available
:type name: str, optional

:return: Return output of ``module avail`` as a list
:rtype: list
:return: Return output of ``module avail`` as a list
:rtype: list
"""

cmd = "module -t avail"
Expand All @@ -121,7 +121,7 @@ def avail(self, name=None):
return ret.stdout.split()

def spider(self, name=None):
""" This method invokes ``module spider`` command. One can specify input arguments
"""This method invokes ``module spider`` command. One can specify input arguments
as a string or list type which are converted into string.

If no arguments are specified to ``Module()`` and spider class we will return the output
Expand Down Expand Up @@ -178,11 +178,10 @@ def spider(self, name=None):
if not isinstance(name, (str, list)):
raise TypeError(f"{name} must be a string or list")


# for list items we convert each item to string and join list into a string
if isinstance(name, list):
name = [str(i) for i in name]
name = ' '.join(name)
name = " ".join(name)

cmd = f"module spider {name}"

Expand All @@ -205,23 +204,23 @@ def spider(self, name=None):
def version(self):
"""Get Lmod version by reading environment variable ``LMOD_VERSION`` and return as a string

:return: Return the Lmod version as a string
:rtype: str
:return: Return the Lmod version as a string
:rtype: str
"""

return os.getenv("LMOD_VERSION") or None

def is_avail(self, name):
"""This method implements the ``module is-avail`` command which is used for checking if a module is available
before loading it. The return value is a 0 or 1.
before loading it. The return value is a 0 or 1.

Parameters:
Parameters:

:param name: argument passed to ``module is-avail``. This is used for checking if module is available
:type name: str, required
:param name: argument passed to ``module is-avail``. This is used for checking if module is available
:type name: str, required

:return: Return output of ``module is-avail``. This checks if module is available and return code is a 0 or 1
:rtype: int
:return: Return output of ``module is-avail``. This checks if module is available and return code is a 0 or 1
:rtype: int
"""

cmd = f"module is-avail {name}"
Expand All @@ -239,24 +238,24 @@ def is_avail(self, name):
def get_command(self):
"""Get the actual module load command that can be used to load the given modules.

:return: return the actual module load command
:rtype: str
:return: return the actual module load command
:rtype: str
"""

return " ".join(self.module_load_cmd)

def test_modules(self, login=False):
"""Test all modules passed to Module class by loading them using ``module load``. The default behavior
is to run the command in a sub-shell but this can be changed to run in a new login shell if ``login=True`` is
specified. The return value is a return code (type ``int``) of the ``module load`` command.
is to run the command in a sub-shell but this can be changed to run in a new login shell if ``login=True`` is
specified. The return value is a return code (type ``int``) of the ``module load`` command.

Parameters:
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
: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

:return: return code of ``module load`` command
:rtype: int
:return: return code of ``module load`` command
:rtype: int
"""

cmd_executed = self.get_command()
Expand All @@ -282,15 +281,15 @@ def test_modules(self, login=False):

def save(self, collection="default"):
"""Save modules specified in Module class into a user collection. This implements the ``module save`` command
for active modules. In this case, we are saving modules that were passed to the Module class. If no argument
is specified, we will save to ``default`` collection, but user can specify a collection name, in that case
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.
for active modules. In this case, we are saving modules that were passed to the Module class. If no argument
is specified, we will save to ``default`` collection, but user can specify a collection name, in that case
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.

Parameters:
Parameters:

:param collection: collection name to save modules. If none specified, ``default`` is the collection.
:type collection: str, optional
:param collection: collection name to save modules. If none specified, ``default`` is the collection.
:type collection: str, optional
"""

# raise TypeError exception if collection is not a string type since that is required
Expand Down Expand Up @@ -318,14 +317,14 @@ def save(self, collection="default"):

def describe(self, collection="default"):
"""Show content of a user collection and implements the command ``module describe``. By default, if no argument
is specified it will resort to showing contents of ``default`` collection. One can pass a collection name
which must be of type ``str`` that is the user collection name. Internally it will run ``module describe <collection>``.
If collection name is not of type ``str``, then an exception of ``TypeError`` will be raised.
is specified it will resort to showing contents of ``default`` collection. One can pass a collection name
which must be of type ``str`` that is the user collection name. Internally it will run ``module describe <collection>``.
If collection name is not of type ``str``, then an exception of ``TypeError`` will be raised.

Parameters:
Parameters:

:param collection: name of user collection to show.
:type collection: str, optional
:param collection: name of user collection to show.
:type collection: str, optional
"""

# raise TypeError exception if collection is not a string type since that is required
Expand All @@ -351,17 +350,17 @@ def describe(self, collection="default"):

def get_collection(self, collection="default"):
"""Return the module command to restore a collection. If no argument is specified, it will resort to the ``default``
collection, otherwise one can specify a collection name of type ``str``. The output will be of type ``str``
such as ``module restore default``. If argument to class is not of type ``str`` then an exception of type
``TypeError`` will be raised.
collection, otherwise one can specify a collection name of type ``str``. The output will be of type ``str``
such as ``module restore default``. If argument to class is not of type ``str`` then an exception of type
``TypeError`` will be raised.

Parameters:
Parameters:

:param collection: collection name to restore
:type collection: str, optional
:param collection: collection name to restore
:type collection: str, optional

:return: return the ``module restore`` command with the collection name
:rtype: str
:return: return the ``module restore`` command with the collection name
:rtype: str
"""

# raise error if collection is not a string
Expand All @@ -372,18 +371,18 @@ def get_collection(self, collection="default"):

def test_collection(self, collection="default"):
"""Test the user collection by running ``module restore`` against a collection name. This is useful, to test a
user collection is working before using it in your scripts. If no argument is specified, it will test the
``default`` collection. One can specify a user collection name which must of be of type ``str`` and it must
exist. The output will be a return code of the ``module restore`` command which would be of type ``int``.
If argument to method is not of type ``str`` an exception of ``TypeError`` will be raised.
user collection is working before using it in your scripts. If no argument is specified, it will test the
``default`` collection. One can specify a user collection name which must of be of type ``str`` and it must
exist. The output will be a return code of the ``module restore`` command which would be of type ``int``.
If argument to method is not of type ``str`` an exception of ``TypeError`` will be raised.

Parameters:
Parameters:

:param collection: collection name to test
:type collection: str, optional
:param collection: collection name to test
:type collection: str, optional

:return: return code of ``module restore`` against the collection name
:rtype: int
:return: return code of ``module restore`` against the collection name
:rtype: int
"""

# raise error if collection is not a string
Expand Down