Source code for pydplus.utils.helper

# -*- coding: utf-8 -*-
"""
:Module:            pydplus.utils.helper
:Synopsis:          Module that allows the pydplus library to leverage a helper configuration file
:Usage:             ``from pydplus.utils import helper``
:Example:           ``helper_settings = helper.get_settings('/tmp/helper.yml', 'yaml')``
:Created By:        Jeff Shurtliff
:Last Modified:     Jeff Shurtliff
:Modified Date:     01 Apr 2026
"""

from __future__ import annotations

import json
import logging
from typing import Optional, Union

import yaml

from .. import constants as const
from .. import errors
from .core_utils import get_file_type

logger = logging.getLogger(__name__)


[docs] def import_helper_file(file_path: str, file_type: str) -> dict: """Import a YAML (.yml, .yaml) or JSON (.json) helper config file. :param file_path: The file path to the YAML file :type file_path: str :param file_type: Defines the file type as ``yaml``, ``yml``, or ``json`` :type file_type: str :returns: The parsed configuration data :raises: :py:exc:`FileNotFoundError`, :py:exc:`pydplus.errors.exceptions.InvalidHelperFileTypeError` """ with open(file_path) as cfg_file: if file_type.replace('.', '') in (const.FILE_EXTENSIONS.YML, const.FILE_EXTENSIONS.YAML): helper_cfg = yaml.safe_load(cfg_file) elif file_type.replace('.', '') == const.FILE_EXTENSIONS.JSON: helper_cfg = json.load(cfg_file) else: logger.error(const._EXCEPTION_CLASSES._INVALID_HELPER_DEFAULT_MSG) raise errors.exceptions.InvalidHelperFileTypeError() logger.info(f'The helper file {file_path} was imported successfully.') return helper_cfg
def _convert_yaml_to_bool(_yaml_bool_value: str) -> bool: """Convert the 'yes' and 'no' YAML values to traditional Boolean values.""" if _yaml_bool_value.lower() in const.HELPER_SETTINGS.VALID_YAML_TRUE_VALUES: return True elif _yaml_bool_value.lower() in const.HELPER_SETTINGS.VALID_YAML_FALSE_VALUES: return False else: _error_msg = 'An invalid Boolean YAML value was provided and cannot be parsed' logger.error(_error_msg) raise ValueError(_error_msg) def _get_connection_info(_helper_cfg: dict) -> dict[str, dict]: """Parse any connection information found in the helper file.""" _connection_info = {const.CONNECTION_INFO.LEGACY: {}, const.CONNECTION_INFO.OAUTH: {}} for _section, _key_list in const.CONNECTION_INFO.CONNECTION_FIELDS.items(): for _key in _key_list: if ( const.HELPER_SETTINGS.CONNECTION in _helper_cfg and _section in _helper_cfg[const.HELPER_SETTINGS.CONNECTION] and _key in _helper_cfg[const.HELPER_SETTINGS.CONNECTION][_section] ): _connection_info[_section][_key] = _helper_cfg[const.HELPER_SETTINGS.CONNECTION][_section][_key] # Parse helper-only OAuth fields that are not persisted in connection_info. if ( const.HELPER_SETTINGS.CONNECTION in _helper_cfg and const.CONNECTION_INFO.OAUTH in _helper_cfg[const.HELPER_SETTINGS.CONNECTION] and isinstance(_helper_cfg[const.HELPER_SETTINGS.CONNECTION][const.CONNECTION_INFO.OAUTH], dict) ): _oauth_section = _helper_cfg[const.HELPER_SETTINGS.CONNECTION][const.CONNECTION_INFO.OAUTH] _scope_preset = _oauth_section.get(const.HELPER_SETTINGS.OAUTH_SCOPE_PRESET) if _scope_preset is None and const.HELPER_SETTINGS.LEGACY_OAUTH_SCOPE_PRESET in _oauth_section: _scope_preset = _oauth_section.get(const.HELPER_SETTINGS.LEGACY_OAUTH_SCOPE_PRESET) logger.warning( f"The helper field '{const.HELPER_SETTINGS.LEGACY_OAUTH_SCOPE_PRESET}' is deprecated in " f"'connection.oauth'; use '{const.HELPER_SETTINGS.OAUTH_SCOPE_PRESET}' instead" ) if _scope_preset is not None: _connection_info[const.CONNECTION_INFO.OAUTH][const.HELPER_SETTINGS.OAUTH_SCOPE_PRESET] = _scope_preset return _connection_info def _collect_values( _top_level_keys: Union[list, tuple, set, frozenset, str], _helper_cfg: dict, _helper_dict: Optional[dict] = None, _ignore_missing: bool = False, ) -> dict: """Loop through a list of top-level keys to collect their corresponding values. :param _top_level_keys: One or more top-level keys that might be found in the helper config file :type _top_level_keys: list, tuple, set, frozenset, str :param _helper_cfg: The configuration parsed from the helper configuration file :type _helper_cfg: dict :param _helper_dict: A predefined dictionary to which the key value pairs should be added :type _helper_dict: dict, None :param _ignore_missing: Indicates whether fields with null values should be ignored (``False`` by default) :type _ignore_missing: bool :returns: A dictionary with the identified key value pairs """ _helper_dict = {} if not _helper_dict else _helper_dict _top_level_keys = (_top_level_keys,) if isinstance(_top_level_keys, str) else _top_level_keys for _key in _top_level_keys: if _key in _helper_cfg: _key_val = _helper_cfg[_key] if _key_val in const.YAML_BOOLEAN_MAPPING: _key_val = const.YAML_BOOLEAN_MAPPING.get(_key_val) _helper_dict[_key] = _key_val elif _key == const.HELPER_SETTINGS.VERIFY_SSL: # Verify SSL certificates by default unless explicitly set to False _helper_dict[_key] = const.HELPER_SETTINGS.DEFAULT_VERIFY_SSL_VALUE else: if not _ignore_missing: _helper_dict[_key] = None return _helper_dict
[docs] def get_helper_settings( file_path: str, file_type: str = const.FILE_EXTENSIONS.JSON, defined_settings: Optional[dict] = None, ) -> dict[str, Union[str, bool, dict]]: """Return a dictionary of the defined helper settings. :param file_path: The file path to the helper configuration file :type file_path: str :param file_type: Defines the helper configuration file as a ``json`` file (default) or a ``yaml``/``yml`` file :type file_type: str :param defined_settings: Core object settings (if any) defined via the ``defined_settings`` parameter :type defined_settings: dict, None :returns: Dictionary of helper variables :raises: :py:exc:`pydplus.errors.exceptions.InvalidHelperFileTypeError` """ # Convert the defined_settings parameter to an empty dictionary if null defined_settings = {} if not defined_settings else defined_settings if file_type not in const.HELPER_SETTINGS.VALID_HELPER_FILE_TYPES: file_type = get_file_type(file_path) # Import the helper configuration file helper_cfg = import_helper_file(file_path, file_type) # Populate the root-level fields that do not require further validation helper_settings = _collect_values(const.HELPER_SETTINGS.ROOT_LEVEL_BASIC_FIELDS, helper_cfg, defined_settings) # Populate the specific admin and auth base URLs if defined if const.HELPER_SETTINGS.BASE_URLS in helper_cfg and isinstance(helper_cfg[const.HELPER_SETTINGS.BASE_URLS], dict): # Add the admin_base_url value to the helper settings if found if const.HELPER_SETTINGS.ADMIN in helper_cfg[const.HELPER_SETTINGS.BASE_URLS]: helper_settings[const.HELPER_SETTINGS.ADMIN_BASE_URL] = helper_cfg[const.HELPER_SETTINGS.BASE_URLS].get( const.HELPER_SETTINGS.ADMIN ) # Add the auth_base_url value to the helper settings if found if const.HELPER_SETTINGS.AUTH in helper_cfg[const.HELPER_SETTINGS.BASE_URLS]: helper_settings[const.HELPER_SETTINGS.AUTH_BASE_URL] = helper_cfg[const.HELPER_SETTINGS.BASE_URLS].get( const.HELPER_SETTINGS.AUTH ) # Populate the connection information in the helper dictionary if const.HELPER_SETTINGS.CONNECTION in helper_cfg and const.HELPER_SETTINGS.CONNECTION not in defined_settings: helper_settings[const.HELPER_SETTINGS.CONNECTION] = _get_connection_info(helper_cfg) # Backward compatibility for older helper files that define oauth_scope_preset at the root level. if const.HELPER_SETTINGS.LEGACY_OAUTH_SCOPE_PRESET in helper_cfg and const.HELPER_SETTINGS.CONNECTION not in defined_settings: _legacy_scope_preset = helper_cfg[const.HELPER_SETTINGS.LEGACY_OAUTH_SCOPE_PRESET] if const.HELPER_SETTINGS.CONNECTION not in helper_settings: helper_settings[const.HELPER_SETTINGS.CONNECTION] = { const.CONNECTION_INFO.LEGACY: {}, const.CONNECTION_INFO.OAUTH: {}, } elif const.CONNECTION_INFO.OAUTH not in helper_settings[const.HELPER_SETTINGS.CONNECTION]: helper_settings[const.HELPER_SETTINGS.CONNECTION][const.CONNECTION_INFO.OAUTH] = {} _oauth_section = helper_settings[const.HELPER_SETTINGS.CONNECTION][const.CONNECTION_INFO.OAUTH] if const.HELPER_SETTINGS.OAUTH_SCOPE_PRESET not in _oauth_section: _oauth_section[const.HELPER_SETTINGS.OAUTH_SCOPE_PRESET] = _legacy_scope_preset logger.warning( f"The helper field '{const.HELPER_SETTINGS.LEGACY_OAUTH_SCOPE_PRESET}' is deprecated; " "use 'connection.oauth.scope_preset' instead" ) # Populate the environment variables information in the helper dictionary if const.HELPER_SETTINGS.ENV_VARIABLES in helper_cfg and const.HELPER_SETTINGS.ENV_VARIABLES not in defined_settings: helper_settings.update(_collect_values(const.HELPER_SETTINGS.ENV_VARIABLES, helper_cfg)) # Return the helper_settings dictionary return helper_settings