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 importlib.util 18import os 19import sys 20import types 21from typing import Any, List, Mapping, Union 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 __contains__(self, k): 38 try: 39 return dict.__contains__(self, k) or hasattr(self, k) 40 except: 41 return False 42 43 def __getattr__(self, k): 44 try: 45 # Throws exception if not in prototype chain 46 return object.__getattribute__(self, k) 47 except AttributeError: 48 try: 49 return self[k] 50 except KeyError: 51 raise AttributeError(k) 52 53 def __setattr__(self, k, v): 54 try: 55 # Throws exception if not in prototype chain 56 object.__getattribute__(self, k) 57 except AttributeError: 58 try: 59 self[k] = v 60 except: 61 raise AttributeError(k) 62 else: 63 object.__setattr__(self, k, v) 64 65 def __delattr__(self, k): 66 try: 67 # Throws exception if not in prototype chain 68 object.__getattribute__(self, k) 69 except AttributeError: 70 try: 71 del self[k] 72 except KeyError: 73 raise AttributeError(k) 74 else: 75 object.__delattr__(self, k) 76 77 78def to_bunch(var: Any) -> Any: 79 """Converts any kind of variable to a Bunch.""" 80 if isinstance(var, list): 81 return [to_bunch(i) for i in var] 82 if isinstance(var, dict): 83 ret = {} 84 for k, v in list(var.items()): 85 if isinstance(v, (list, dict)): 86 v = to_bunch(v) 87 ret[k] = v 88 return Bunch(ret) 89 else: 90 return var 91 92 93def merge_json(dst: Union[Mapping, List], add: Union[Mapping, List]) -> None: 94 """Merges JSON objects recursively.""" 95 if isinstance(dst, dict) and isinstance(add, dict): 96 for k, v in list(add.items()): 97 if k in dst: 98 if k.startswith("#"): 99 continue 100 merge_json(dst[k], v) 101 else: 102 dst[k] = v 103 elif isinstance(dst, list) and isinstance(add, list): 104 dst.extend(add) 105 else: 106 raise TypeError( 107 "Tried to merge incompatible objects %s %s\n\n%r\n\n%r" 108 % (type(dst).__name__, type(add).__name__, dst, add) 109 ) 110