def _assign_default_values(self, schema, config):
        """
        Assign default values for particular config if default values are provided in the config
        schema and a value is not specified in the config.

        Note: This method mutates config argument in place.

        :rtype: ``dict|list``
        """
        schema_is_dict = isinstance(schema, dict)
        iterator = schema.items() if schema_is_dict else enumerate(schema)

        # _get_*_schema ensures that schema_item is always a dict
        for schema_item_key, schema_item in iterator:
            has_default_value = "default" in schema_item
            if isinstance(config, dict):
                has_config_value = schema_item_key in config
            else:
                has_config_value = schema_item_key < len(config)

            default_value = schema_item.get("default", None)
            if has_default_value and not has_config_value:
                # Config value is not provided, but default value is, use a default value
                config[schema_item_key] = default_value

            try:
                config_value = config[schema_item_key]
            except (KeyError, IndexError):
                config_value = None

            schema_item_type = schema_item.get("type", None)

            if schema_item_type == "object":
                has_properties = schema_item.get("properties", None)
                has_pattern_properties = schema_item.get("patternProperties", None)
                has_additional_properties = schema_item.get(
                    "additionalProperties", None
                )

                # Inspect nested object properties
                if (
                    has_properties
                    or has_pattern_properties
                    or has_additional_properties
                ):
                    if not config_value:
                        config_value = config[schema_item_key] = {}

                    properties_schema = self._get_object_properties_schema(
                        schema_item,
                        object_keys=config_value.keys(),
                    )

                    self._assign_default_values(
                        schema=properties_schema, config=config_value
                    )
            elif schema_item_type == "array":
                has_items = schema_item.get("items", None)
                has_additional_items = schema_item.get("additionalItems", None)

                # Inspect nested array items
                if has_items or has_additional_items:
                    if not config_value:
                        config_value = config[schema_item_key] = []

                    items_schema = self._get_array_items_schema(
                        schema_item,
                        items_count=len(config_value),
                    )
                    self._assign_default_values(
                        schema=items_schema, config=config_value
                    )

        return config

        Assign default values for particular config if default values are provided in the config
        schema and a value is not specified in the config.

        Note: This method mutates config argument in place.

        :rtype: ``dict|list``
        
st2common/st2common/util/config_loader.py