1# Copyright 2023 The Pigweed Authors 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4# use this file except in compliance with the License. You may obtain a copy of 5# the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12# License for the specific language governing permissions and limitations under 13# the License. 14"""Reads and parses the Pigweed config file. 15 16See also https://pigweed.dev/seed/0101-pigweed.json.html. 17""" 18 19from __future__ import annotations 20 21from collections.abc import Mapping 22import json 23import os 24from pathlib import Path 25from typing import Any 26 27try: 28 # This will only succeed when using config_file from Bazel. 29 from python.runfiles import runfiles # type: ignore 30except (ImportError, ModuleNotFoundError): 31 runfiles = None 32 33 34def _resolve_env(env: Mapping[str, str] | None) -> Mapping[str, str]: 35 if env: 36 return env 37 return os.environ 38 39 40def _get_project_root(env: Mapping[str, str]) -> Path: 41 for var in ('PW_PROJECT_ROOT', 'PW_ROOT'): 42 if var in env: 43 return Path(env[var]) 44 raise ValueError('environment variable PW_PROJECT_ROOT not set') 45 46 47def _pw_env_substitute(env: Mapping[str, str], string: Any) -> str: 48 if not isinstance(string, str): 49 return string 50 51 # Substitute in environment variables based on $pw_env{VAR_NAME} tokens. 52 for key, value in env.items(): 53 string = string.replace('$pw_env{' + key + '}', value) 54 55 if '$pw_env{' in string: 56 raise ValueError(f'Unresolved $pw_env{{...}} in JSON string: {string}') 57 58 return string 59 60 61def path(env: Mapping[str, str] | None = None) -> Path: 62 """Return the path where pigweed.json should reside.""" 63 env = _resolve_env(env) 64 return _get_project_root(env=env) / 'pigweed.json' 65 66 67def path_from_runfiles() -> Path: 68 r = runfiles.Create() 69 location = r.Rlocation("pigweed.json") 70 if location is None: 71 # Failed to find pigweed.json 72 raise ValueError("Failed to find pigweed.json") 73 74 return Path(location) 75 76 77def load(env: Mapping[str, str] | None = None) -> dict: 78 """Load pigweed.json if it exists and return the contents.""" 79 if env is None and runfiles is not None: 80 config_path = path_from_runfiles() 81 else: 82 env = _resolve_env(env) 83 config_path = path(env=env) 84 85 if not os.path.isfile(config_path): 86 return {} 87 88 def hook(obj): 89 out = {} 90 for key, val in obj.items(): 91 out[key] = _pw_env_substitute(env, val) 92 return out 93 94 with open(config_path, 'r') as file: 95 return json.load(file, object_hook=hook) 96