Source code for c3.utils.utils

"""Miscellaneous, general utilities."""
import time
import os
import tempfile
import numpy as np
from typing import List, Tuple
import warnings


# SYSTEM AND SETUP
[docs]def log_setup(data_path: str = None, run_name: str = "run") -> str: """ Make sure the file path to save data exists. Create an appropriately named folder with date and time. Also creates a symlink "recent" to the folder. Parameters ---------- data_path : str File path of where to store any data. run_name : str User specified name for the run. Returns ------- str The file path to store new data. """ if data_path: data_path = os.path.abspath(data_path) else: data_path = os.path.join(tempfile.TemporaryDirectory().name, "c3logs") if not os.path.isdir(data_path): os.makedirs(data_path) pwd = os.path.join( data_path, run_name, time.strftime("%Y_%m_%d_T_%H_%M_%S", time.localtime()) ) while os.path.exists(pwd): time.sleep(1) pwd = os.path.join( data_path, run_name, time.strftime("%Y_%m_%d_T_%H_%M_%S", time.localtime()) ) os.makedirs(pwd) recent = os.path.join(data_path, "recent") replace_symlink(pwd, recent) return os.path.join(pwd, "")
# NICE PRINTING FUNCTIONS
[docs]def eng_num(val: float) -> Tuple[float, str]: """Convert a number to engineering notation by returning number and prefix.""" if np.array(val).size > 1: return np.array(val), "" if np.isnan(val): return np.nan, "NaN" big_units = ["", "K", "M", "G", "T", "P", "E", "Z"] small_units = ["m", "ยต", "n", "p", "f", "a", "z"] sign = 1 if val == 0: return 0, "" if val < 0: val = -val sign = -1 tmp = np.log10(val) idx = int(tmp // 3) if tmp < 0: if np.abs(idx) > len(small_units): return val * (10 ** (3 * len(small_units))), small_units[-1] prefix = small_units[-(idx + 1)] else: if np.abs(idx) > len(big_units) - 1: return val * (10 ** (-3 * (len(big_units) - 1))), big_units[-1] prefix = big_units[idx] return sign * (10 ** (tmp % 3)), prefix
[docs]def num3str(val: float, use_prefix: bool = True) -> str: """Convert a number to a human readable string in engineering notation.""" if np.array(val).size > 1: return np.array2string(val, precision=3) if use_prefix: num, prefix = eng_num(val) formatted_string = f"{num:.3f} " + prefix else: formatted_string = f"{val:.3} " return formatted_string
# USER INTERACTION
[docs]def ask_yn() -> bool: """Ask for y/n user decision in the command line.""" asking = True text = input("(y/n): ") if text == "y": asking = False boolean = True elif text == "n": asking = False boolean = False while asking: text = input("Please write y or n and press enter: ") if text == "y": asking = False boolean = True elif text == "n": asking = False boolean = False return boolean
[docs]def deprecated(message: str): """Decorator for deprecating functions Parameters ---------- message : str Message to display along with DeprecationWarning Examples -------- Add a :code:`@deprecated("message")` decorator to the function:: @deprecated("Using standard width. Better use gaussian_sigma.") def gaussian(t, params): ... """ def deprecated_decorator(func): def deprecated_func(*args, **kwargs): warnings.warn( "{} is a deprecated function. {}".format(func.__name__, message), category=DeprecationWarning, stacklevel=2, ) warnings.simplefilter("default", DeprecationWarning) return func(*args, **kwargs) return deprecated_func return deprecated_decorator
[docs]def flatten(lis: List, ltypes=(list, tuple)) -> List: """Flatten lists of arbitrary lengths https://rightfootin.blogspot.com/2006/09/more-on-python-flatten.html Parameters ---------- lis : List The iterable to flatten ltypes : tuple, optional Possibly the datatype of the iterable, by default (list, tuple) Returns ------- List Flattened list """ ltype = type(lis) lis = list(lis) i = 0 while i < len(lis): while isinstance(lis[i], ltypes): if not lis[i]: lis.pop(i) i -= 1 break else: lis[i : i + 1] = lis[i] i += 1 return ltype(lis)