import sys import inspect import hashlib import logging from functools import partial import random from typing import Callable import importlib import numpy as np def get_logger(module_name: str, filename: str = "main.log") -> logging.Logger: """ Returns a logger for the given module name and filename. :param module_name: name of the module, as string :param filename: name of the logging file, as string :return: the logger, as logging.Logger object """ logger = logging.getLogger(module_name) logger.setLevel(logging.DEBUG) logger.propagate = False formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler = logging.FileHandler(filename) file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(formatter) logger.addHandler(file_handler) # add system out handler stream_handler = logging.StreamHandler(sys.stdout) stream_handler.setLevel(logging.INFO) stream_handler.setFormatter(formatter) logger.addHandler(stream_handler) return logger def get_variable_from_module(module_path: str, variable_name: str): """ Get a variable from a module by its name during runtime Args: module_path: module to fetch variable from variable_name: variable to fetch from module Returns: variable from module """ module = importlib.import_module(module_path) variable = getattr(module, variable_name) if variable is None: raise ValueError(f"Variable {variable_name} not found in module {module_path}") return variable def get_callable_name(callable_obj): """ Get the name of the callable, handling `functools.partial`. :param callable_obj: callable object :return: name of the callable """ if isinstance(callable_obj, partial): func_name = callable_obj.func.__name__ args = ", ".join(object_to_string(arg) for arg in callable_obj.args) kwargs = ", ".join(f"{object_to_string(k)}={object_to_string(v)!r}" for k, v in callable_obj.keywords.items()) return f"partial({func_name}, {args}, {kwargs})" else: if hasattr(callable_obj, '__name__'): return callable_obj.__name__ elif hasattr(callable_obj, '__class__'): return callable_obj.__class__.__name__ elif hasattr(callable_obj, '__hash__'): return callable_obj.__hash__ else: raise ValueError(f"Could not determine name of callable object {callable_obj}") def object_to_string(value, skip_types=None): """ Convert any object to a string representation that avoids memory addresses. Handles complex data types recursively. :param value: object to convert :param skip_types: types to skip during conversion :return: string representation of the object """ if skip_types is None: skip_types = [] if any(isinstance(value, t) for t in skip_types): return 'skipped_type' elif isinstance(value, (str, int, float, bool)): # Handle primitive data types directly return repr(value) elif isinstance(value, dict): return '{' + ', '.join(f"{k}: {object_to_string(v, skip_types)}" for k, v in value.items()) + '}' elif isinstance(value, (list, tuple)): return '[' + ', '.join(object_to_string(item, skip_types) for item in value) + ']' elif isinstance(value, partial): return get_callable_name(value) elif inspect.isclass(value): return f"" elif hasattr(value, '__class__') and not value.__class__ != "function": # Correct handling for instances of classes, but not functions return f"" elif isinstance(value, Callable): return f"" else: return repr(value) def get_config_id(configuration: dict) -> str: """ Generate a somewhat unique human-readable model name from the model and training parameters. :param configuration: dictionary containing model and training parameters :return: human-readable model name """ adjectives = ["autumn", "hidden", "bitter", "misty", "silent", "empty", "dry", "dark", "summer", "icy", "delicate", "quiet", "white", "black", "blue", "green", "red", "yellow", "purple", "orange", "pink", "golden", "silver", "crimson", "violet", "azure", "amber", "sapphire", "emerald", "ruby", "pearl", "topaz", "onyx", "turquoise", "citrine", ] nouns = ["waterfall", "river", "breeze", "moon", "rain", "wind", "sea", "morning", "snow", "lake", "sunset", "pine", "shadow", "leaf", "dawn", "glitter", "forest", "cloud", "sky", "sun", "butterfly", "flower", "bird", "mountain", "valley", "ocean", "star", "night", "dream", "whisper", "echo", "horizon", "wave", "petal", "dew", "mist"] # also add dataset config, but skip functions base_name = object_to_string(configuration) # hash long name basename_hash = hashlib.md5(base_name.encode()).hexdigest() # select adjective and noun based on hash random.seed(int(basename_hash, 16)) model_name = f"{random.choice(adjectives)}_{random.choice(nouns)}_{basename_hash[:5]}" return model_name def convert_for_json(obj): if isinstance(obj, dict): return {k: convert_for_json(v) for k, v in obj.items()} elif isinstance(obj, list): return [convert_for_json(v) for v in obj] elif isinstance(obj, np.generic): return obj.item() else: return obj