| import logging |
| import os |
| from typing import Optional |
|
|
|
|
| def is_float(value: str): |
| """Check if the input value is float. |
| |
| :param value: value |
| :return: True / False based on whether the value is float or not |
| """ |
| try: |
| float(value) |
| except (TypeError, ValueError): |
| return False |
| else: |
| return True |
|
|
|
|
| def is_int(value: str): |
| """Check if the input value is int. |
| |
| :param value: value |
| :return: True / False based on whether the value is int or not |
| """ |
| try: |
| float_value = float(value) |
| int_value = int(value) |
| except (TypeError, ValueError): |
| return False |
| else: |
| return float_value == int_value |
|
|
|
|
| def is_list(value: str): |
| """Check if the input value is list. |
| |
| Currently, we support list in the following format - |
| |
| 1. "a,b,c,d" |
| 2. "1,2,3,4" |
| |
| :param value: value |
| :return: True / False based on whether the value is list or not |
| """ |
| return "," in value |
|
|
|
|
| def is_boolean(value: str): |
| """Check if the input value is boolean. |
| |
| :param value: value |
| :return: True / False based on whether the value is boolean or not |
| """ |
| return value.lower() in ["true", "false"] |
|
|
|
|
| def parse_boolean(value: str): |
| """Parse the boolean value. |
| |
| :param value: value |
| :return: Parsed boolean values |
| """ |
| return True if value.lower() == "true" else False |
|
|
|
|
| def parse_list(value: str): |
| """Parse the list value. |
| |
| Currently, we support list in the following format - |
| |
| 1. "a,b,c,d" |
| 2. "1,2,3,4" |
| |
| :param value: value |
| :return: Parsed list |
| """ |
| values = value.split(",") |
| clean_values = [v.strip(" \"'") for v in values] |
| return [infer_type_and_cast_value(v) for v in clean_values] |
|
|
|
|
| def infer_type_and_cast_value(value: Optional[str]): |
| """Infer the type of value and casts it accordingly. |
| |
| :param value: value |
| :return: casted value |
| """ |
| if value is None: |
| return value |
| elif is_int(value): |
| return int(value) |
| elif is_float(value): |
| return float(value) |
| elif is_boolean(value): |
| return parse_boolean(value) |
| elif is_list(value): |
| return parse_list(value) |
| else: |
| return value |
|
|
|
|
| def __setup_fault_handler(file_path: str = None): |
| """Set up fault handler. |
| |
| :param file_path: path to the error file |
| :return: |
| """ |
| try: |
| import faulthandler |
|
|
| if not faulthandler.is_enabled(): |
| if file_path is not None: |
| faulthandler.enable(os.open(file_path, os.O_APPEND), all_threads=True) |
| else: |
| faulthandler.enable() |
| except ImportError: |
| logging.warn("No faulthandler found") |
|
|
|
|
| def get_error_logger(): |
| """Return the logger from logging for id ERROR_LOGGER_ID .""" |
| return logging.getLogger("error") |
|
|
|
|
| def setup_trusted_log(error_volume: str, error_file_path: str): |
| """Set up trusted logs for the script. |
| |
| :param error_volume: volume where the errors should be written |
| :param error_file_path: path to the error_file |
| :return: trusted logger |
| """ |
| trusted_log_formatter = logging.Formatter( |
| "[%(asctime)s %(levelname)s %(thread)d %(filename)s:%(lineno)d] %(message)s", |
| datefmt="%m/%d/%Y %H:%M:%S", |
| ) |
| os.makedirs(error_volume, exist_ok=True) |
| trusted_log_handler = logging.FileHandler(error_file_path) |
| __setup_fault_handler(file_path=error_file_path) |
| trusted_log_handler.setFormatter(trusted_log_formatter) |
| trusted_log_handler.setLevel(logging.INFO) |
|
|
| error_logger = get_error_logger() |
| error_logger.addHandler(trusted_log_handler) |
| error_logger.propagate = False |
|
|
|
|
| def write_trusted_log_info(private_info_message): |
| """Write private info message to the trusted log channel. |
| |
| :param private_info_message: private trusted log message |
| :return: |
| """ |
| trusted_logger = get_error_logger() |
| trusted_logger.info(private_info_message) |
|
|
|
|
| def write_failure_reason(failure_reason_text, file_path): |
| """Write failure reason to failure file. |
| |
| :param failure_reason_text: reason for failure |
| :param file_path: path to the failure file |
| :return: |
| """ |
| if not os.path.exists(os.path.dirname(file_path)): |
| os.makedirs(os.path.dirname(file_path)) |
| with open(file_path, "w") as f: |
| f.write(failure_reason_text) |
|
|
|
|
| def write_trusted_log_exception( |
| error_message, caused_by, failure_file_path, failure_prefix="Algorithm Error" |
| ): |
| """Write private exception message to the trusted error channel. |
| |
| :param error_message: error_message |
| :param caused_by: cause for the error |
| :param failure_file_path: failure file path. Usually /opt/ml/output/failure |
| :param failure_prefix: prefix to attach to the error message |
| :return: |
| """ |
| message = "{}: {}".format(failure_prefix, error_message) |
| error_detail = "Caused by: {}".format(caused_by) |
| message += "\n\n{}".format(error_detail) |
| err_logger = get_error_logger() |
| err_logger.exception(message) |
| write_failure_reason(message, failure_file_path) |
| return message |
|
|