1#!/usr/bin/env python3 2# Copyright 2020 The gRPC Authors 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Utility functions for build file generation scripts.""" 16 17import os 18import sys 19import types 20import importlib.util 21from typing import Any, Union, Mapping, List 22 23 24def import_python_module(path: str) -> types.ModuleType: 25 """Imports the Python file at the given path, returns a module object.""" 26 module_name = os.path.basename(path).replace('.py', '') 27 spec = importlib.util.spec_from_file_location(module_name, path) 28 module = importlib.util.module_from_spec(spec) 29 sys.modules[module_name] = module 30 spec.loader.exec_module(module) 31 return module 32 33 34class Bunch(dict): 35 """Allows dot-accessible dictionaries.""" 36 37 def __init__(self, d: Mapping): 38 dict.__init__(self, d) 39 self.__dict__.update(d) 40 41 42def to_bunch(var: Any) -> Any: 43 """Converts any kind of variable to a Bunch.""" 44 if isinstance(var, list): 45 return [to_bunch(i) for i in var] 46 if isinstance(var, dict): 47 ret = {} 48 for k, v in var.items(): 49 if isinstance(v, (list, dict)): 50 v = to_bunch(v) 51 ret[k] = v 52 return Bunch(ret) 53 else: 54 return var 55 56 57def merge_json(dst: Union[Mapping, List], add: Union[Mapping, List]) -> None: 58 """Merges JSON objects recursively.""" 59 if isinstance(dst, dict) and isinstance(add, dict): 60 for k, v in add.items(): 61 if k in dst: 62 if k.startswith('#'): 63 continue 64 merge_json(dst[k], v) 65 else: 66 dst[k] = v 67 elif isinstance(dst, list) and isinstance(add, list): 68 dst.extend(add) 69 else: 70 raise TypeError( 71 'Tried to merge incompatible objects %s %s\n\n%r\n\n%r' % 72 (type(dst).__name__, type(add).__name__, dst, add)) 73