powertrain-build/powertrain_build/lib/helper_functions.py
olindgre 2ece01e1d7 Make powertrain-build not overlap with pybuild in site-packages
Change-Id: I7b59f3f04f0f787d35db0b9389f295bf1ad24f56
2024-09-17 10:25:04 +02:00

128 lines
3.9 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Module for various helper functions."""
import json
import collections
from pathlib import Path
from subprocess import getoutput
def get_repo_root():
""" Return absolute path to repository where script is executed, regardless
of the current script's location.
Returns:
path (str): Absolute, canonical path to the top-level repository.
if not a git repository, returns current working dir
"""
try:
root = Path(getoutput('git rev-parse --show-toplevel')).resolve()
except (FileNotFoundError, OSError):
root = Path.cwd().resolve()
return str(root)
def create_dir(path: Path):
"""If the directory for a given directory path does not exist, create it.
Including parent directories.
Args:
path (Path): Path to directory.
Returns:
path (Path): Path to directory.
"""
if not path.is_dir():
path.mkdir(parents=True)
return path
def merge_dicts(dict1, dict2, handle_collision=lambda a, b: a, merge_recursively=False):
"""Merge two dicts.
Args:
dict1 (dict): dict to merge
dict2 (dict): dict to merge
handle_collision (function(arg1, arg2)): function to resolve key collisions,
default keeps original value in dict1
merge_recursively (bool): if set to True it merges nested dicts recursively with handle_collision
resolving collisions with non-dict types.
Returns:
dict: the result of the two merged dicts
"""
result = dict1.copy()
for key, value in dict2.items():
if key not in result:
result.update({key: value})
elif isinstance(result[key], dict) and \
isinstance(value, dict) and \
merge_recursively:
result[key] = merge_dicts(result[key], value, handle_collision, merge_recursively)
else:
result[key] = handle_collision(result[key], value)
return result
def deep_dict_update(base, add):
"""Recursively update a dict that may contain sub-dicts.
Args:
base (dict): The base dict will be updated with the contents
of the add dict
add (dict): This dict will be added to the base dict
Returns:
dict: the updated base dict is returned
"""
for key, value in add.items():
if key not in base:
base[key] = value
elif isinstance(value, dict):
deep_dict_update(base[key], value)
return base
def deep_json_update(json_file, dict_to_merge):
""" Recursively update a json file with the content of a dict.
Args:
json_file (path): json file.
dict_to_merge (dict): Dictionary that will be merged into json file, overwriting values and adding keys.
"""
with open(json_file, 'r', encoding="utf-8") as fle:
json_dict = json.load(fle)
merged_dict = merge_dicts(
json_dict,
dict_to_merge,
handle_collision=lambda a, b: b,
merge_recursively=True
)
with open(json_file, 'w', encoding="utf-8") as fle:
json.dump(merged_dict, fle, indent=2)
def recursive_default_dict():
"""Returns recursively defined default dict. This allows people to insert
arbitrarily complex nested data into the dict without getting KeyErrors.
Returns:
defaultdict(self): A new defaultdict instance, recursively defined.
"""
return collections.defaultdict(recursive_default_dict)
def to_normal_dict(weird_dict):
"""Converts nested dict to normal, suitable for YAML/JSON dumping.
Args:
weird_dict (dict): Any dict-like item that can be converted to a dict.
Returns:
dict(nested): An identical nested dict structure, but using real dicts.
"""
for key, value in weird_dict.items():
if isinstance(value, dict):
weird_dict[key] = to_normal_dict(value)
return dict(weird_dict)