Skip to content

Commit

Permalink
Introducing utility function libs
Browse files Browse the repository at this point in the history
  • Loading branch information
xandrd committed Nov 14, 2023
1 parent f71820b commit 7006e55
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
62 changes: 62 additions & 0 deletions pyspedas/utilities/libs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
Displays location of source files, one line of documentation and the function name based on the request
"""

import importlib
import pkgutil
import inspect
import pyspedas

def libs(function_name, package=pyspedas):
"""
Searches for a specified function within a given package and its submodules,
and prints information about the function if found. The search is performed
utilizing the imports defined in each __init__.py package file to display the
callable function name.
Parameters:
- function_name (str): The name or partial name of the function to search for.
- package (module, optional): The Python package in which to search for the function.
Default is the pyspedas package. This should be a Python module object.
Note:
- All submodules of pyspedas are imported during the search. The package option is
simply narrows the search.
- The function specifically searches for functions, not classes or other objects.
- If multiple functions with the same name exist in different modules within the package,
it will list them all.
- The function handles ImportError exceptions by printing an error message and
continuing the search.
Example Usage:
```python
pyspedas.utilities.libs('fgm')
pyspedas.utilities.libs('fgm', package=pyspedas.mms)
```
"""

# Gate for no function_name
if not function_name:
return

def list_functions(module, root, function_name, pacakge_obj):
full_module_name = module.__name__
for name, obj in inspect.getmembers(module):
if inspect.isfunction(obj) and (function_name in name) and (pacakge_obj.__name__ in obj.__module__):
full_name = full_module_name + '.' + name
source_file = inspect.getsourcefile(obj)
doc = inspect.getdoc(obj)
first_line_of_doc = doc.split('\n')[0] if doc else "No documentation"
print(f"Function: {full_name}\nLocation: {source_file}\nDocumentation: {first_line_of_doc}\n")

def traverse_modules(package, function_name, pacakge_obj):
for importer, modname, ispkg in pkgutil.walk_packages(path=package.__path__, prefix=package.__name__ + '.'):
if ispkg:
try:
module = importlib.import_module(modname)
list_functions(module, package, function_name, pacakge_obj)
except ImportError as e:
print(f"Error importing module {modname}: {e}")

traverse_modules(pyspedas, function_name, package)
50 changes: 50 additions & 0 deletions pyspedas/utilities/tests/libs_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import unittest
from io import StringIO
import sys
import pyspedas
from pyspedas.utilities.libs import libs


class LibsTestCase(unittest.TestCase):

def setUp(self):
# Call function once to import all the submodules to silence the output
try:
libs('')
except Exception as e:
self.fail("Unexpected exception %s" % e)

# Redirect stdout to capture print statements
self.held_stdout = sys.stdout
sys.stdout = StringIO()


def tearDown(self):
# Reset stdout
sys.stdout = self.held_stdout

def test_known_function_fgm(self):
libs('fgm')
output = sys.stdout.getvalue()
self.assertIn('fgm', output)
self.assertIn('Function:', output)

def test_non_existent_function(self):
libs('non_existent_function')
output = sys.stdout.getvalue()
self.assertEqual(output, '') # Assuming no output for non-existent functions

def test_partial_function_name(self):
libs('mms_')
output = sys.stdout.getvalue()
self.assertIn('mms_', output)

def test_different_package(self):
libs('fgm', package=pyspedas.themis)
output = sys.stdout.getvalue()
self.assertIn('fgm', output)
self.assertNotIn('mms', output)


if __name__ == '__main__':
unittest.main()

0 comments on commit 7006e55

Please sign in to comment.