Source code for utilipy.utils.misc
# -*- coding: utf-8 -*-
"""Miscellaneous Utility Functions."""
__all__ = [
# functions
"temporary_namespace",
"make_help_function",
]
##############################################################################
# IMPORTS
# BUILT-IN
import typing as T
from contextlib import contextmanager
from types import ModuleType
# THIRD PARTY
from astropy.utils.data import find_current_module
##############################################################################
# CODE
##############################################################################
[docs]@contextmanager
def temporary_namespace(locals_ref, keep: T.List[str] = []):
"""Temporary Namespace within ``with`` statement.
1. copies current namespace from `locals_ref`
2. Enters ``with`` statement
3. restores original namespace except those specified in `keep`
Parameters
----------
module : module
``sys.modules[__name__]`` of module calling from.
.. todo::
not need to pass any module information. infer.
keep : list, optional
list of (str) variable names to keep.
Yields
------
locals_ref : locals
for accessing namespace
Warnings
--------
Does NOT work within functions.
"""
original_namespace = locals_ref.copy()
try:
yield locals_ref
finally:
# difference of current and old namespace
# without the keep keys
drop_keys = (
set(locals_ref.keys())
.difference(original_namespace.keys())
.difference(keep)
)
# kept values
keep_dict = {k: locals_ref[k] for k in keep}
# print("drop_keys", drop_keys, "keep_dict", keep_dict)
# Restoring Namespace
original_namespace.update(keep_dict) # add keep values to original
locals_ref.update(original_namespace) # restore original (+kept)
for key in drop_keys: # drop unkept context-specific values
locals_ref.pop(key)
# /try
# -------------------------------------------------------------------
[docs]def make_help_function(
name: str,
module: T.Union[None, ModuleType, str] = None,
look_for: T.Optional[str] = None, # "Routine Listings",
doctitle: T.Optional[str] = None,
) -> T.Callable:
"""Set docstring from module Returns section.
Takes a helper function for a module and adds the content of the modules'
`look_for` section. Currently only works on numpy-style docstring.
Parameters
----------
name: str
name of function. Add "_help".
module:
Module
look_for : str, optional
The section to look for (default None)
The section name "Routine Listings" is replaced by "Returns"
doctitle : str, optional
Returns
-------
decorator : Callable
decorator function to change the wrapped function's docstring.
Raises
------
TypeError
if `look_for` is not None or str
Notes
-----
.. todo::
separate the imports help function from the general helps function.
the general help function should be similar to the find_api_page
in astropy and the help function in the utilipy init.
"""
if module is None:
module = find_current_module(2)
mod_name = module.__name__
module_doc = module.__doc__
elif isinstance(module, ModuleType):
module_doc = module.__doc__
mod_name = module.__name__
elif isinstance(module, str):
module_doc = module
mod_name = find_current_module(2).__name__
if look_for is None:
doc = module_doc
elif isinstance(look_for, str):
ind = module_doc.find(look_for) + 2 * len(look_for) + 2
end_ind = ind + module_doc[ind:].find("---") # finding next section
doc = module_doc[ind:end_ind] # get section (+ next header)
doc = "\n".join(doc.split("\n")[:-2]) # strip next header
if look_for == "Routine Listings": # skip 'Routine Listings' & line
# Name = name.capitalize()
doc = f"\nReturns\n{'-'*(len(name) + 1)}-------\n" + doc
else:
raise TypeError
# TODO with FunctionType in types
def help_function():
print(doc)
# help_function._doc = doc
help_function.__name__ = f"{name}_help"
help_function.__module__ = mod_name
help_function.__doc__ = f"Help for {doctitle or name}.\n\n" + (doc or "")
# /def
return help_function
# /def
##############################################################################
# END