xref: /aosp_15_r20/external/grpc-grpc/tools/buildgen/_utils.py (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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