#!/usr/bin/env python
"""Generic program utilities"""
from __future__ import annotations
import importlib
import warnings
from itertools import zip_longest
import numpy as np
import numpy.typing as npt
from astropy.utils.exceptions import AstropyWarning
from spectral_cube.utils import SpectralCubeWarning
warnings.filterwarnings(action="ignore", category=SpectralCubeWarning, append=True)
warnings.simplefilter("ignore", category=AstropyWarning)
# From https://stackoverflow.com/questions/58065055/floor-and-ceil-with-number-of-decimals#:~:text=The%20function%20np.,a%20number%20with%20zero%20decimals.
[docs]
def my_ceil(
a: npt.NDArray[np.float64], precision: float = 0
) -> npt.NDArray[np.float64]:
return np.true_divide(np.ceil(a * 10**precision), 10**precision)
[docs]
def my_floor(
a: npt.NDArray[np.float64], precision: float = 0
) -> npt.NDArray[np.float64]:
return np.true_divide(np.floor(a * 10**precision), 10**precision)
# From https://stackoverflow.com/questions/1176136/convert-string-to-python-class-object
[docs]
def class_for_name(module_name: str, class_name: str) -> object:
"""Returns a class object given a module name and class name
Args:
module_name (str): Module name
class_name (str): Class name
Returns:
object: Class object
"""
# load the module, will raise ImportError if module cannot be loaded
m = importlib.import_module(module_name)
# get the class, will raise AttributeError if class cannot be found
c = getattr(m, class_name)
return c
# stolen from https://stackoverflow.com/questions/32954486/zip-iterators-asserting-for-equal-length-in-python
[docs]
def zip_equal(*iterables):
sentinel = object()
for combo in zip_longest(*iterables, fillvalue=sentinel):
if sentinel in combo:
raise ValueError("Iterables have different lengths")
yield combo
[docs]
def yes_or_no(question: str) -> bool:
"""Ask a yes or no question via input()
Args:
question (str): Question to ask
Returns:
bool: True for yes, False for no
"""
while "Please answer 'y' or 'n'":
reply = str(input(question + " (y/n): ")).lower().strip()
if reply[:1] == "y":
return True
elif reply[:1] == "n":
return False
else:
raise ValueError("Please answer 'y' or 'n'")