1d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# SPDX-License-Identifier: Apache-2.0 2d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# 3d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Copyright (C) 2015, ARM Limited and contributors. 4d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# 5d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Licensed under the Apache License, Version 2.0 (the "License"); you may 6d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# not use this file except in compliance with the License. 7d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# You may obtain a copy of the License at 8d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# 9d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# http://www.apache.org/licenses/LICENSE-2.0 10d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# 11d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# Unless required by applicable law or agreed to in writing, software 12d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# See the License for the specific language governing permissions and 15d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# limitations under the License. 16d95e98d88a6cf4347853427dc4ef6fd0222a3881Patrick Bellasi# 17eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 18eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiimport json 19eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiimport os 20eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiimport re 21c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiimport logging 22c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiimport logging.config 23c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 24c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 25c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasiclass LisaLogging(object): 26c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 27c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi @classmethod 28c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi def setup(self, filepath='logging.conf', level=logging.INFO): 29c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi """ 30c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi Initialize logging used for all the LISA modules. 31c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 32c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi :param filepath: the relative or absolute path of the logging configuration to use. 33c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi Relative path uses the LISA_HOME environment variable 34c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi has base folder. 35c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi :type filepath: str 36c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 37c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi :param level: the default log level to enable, INFO by default 38c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi :type level: logging.<level> or int in [0..50] 39c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi """ 40c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 41c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi # Load the specified logfile using an absolute path 42c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi basepath = os.path.dirname(__file__).replace('/libs/utils', '') 43c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi filepath = os.path.join(basepath, filepath) 44c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi if not os.path.exists(filepath): 45c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi raise ValueError('Logging configuration file not found in: {}'\ 46c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi .format(filepath)) 47c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi logging.config.fileConfig(filepath) 48c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi logging.getLogger().setLevel(level) 49c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 50c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi logging.info('Using LISA logging configuration:') 51c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi logging.info(' %s', filepath) 52eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 53eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasiclass JsonConf(object): 5404cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman """ 5504cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman Class for parsing a JSON superset with comments. 5604cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 5704cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman Simply strips comments and then uses the standard JSON parser. 5804cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 5904cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman :param filename: Path to file to parse 6004cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman :type filename: str 6104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman """ 62eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 63eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi def __init__(self, filename): 64eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi self.filename = filename 65eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi self.json = None 66eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 67eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi def load(self): 6804cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman """ 6904cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman Parse a JSON file 7004cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 7104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman First remove comments and then use the json module package 7204cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman Comments look like : 7304cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 7404cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman :: 7504cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 7604cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman // ... 7704cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 7804cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman or 7904cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 8004cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman :: 8104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 8204cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman /* 8304cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman ... 8404cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman */ 8504cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman 86eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi """ 87c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 88c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi # Setup logging 89c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi self._log = logging.getLogger('JsonConf') 90c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi 91eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi if not os.path.isfile(self.filename): 92eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi raise RuntimeError( 93eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 'Missing configuration file: {}'.format(self.filename) 94eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi ) 95c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi self._log.debug('loading JSON...') 96eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 97eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi with open(self.filename) as fh: 98eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi content = ''.join(fh.readlines()) 99eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 100eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi ## Looking for comments 101eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi match = JSON_COMMENTS_RE.search(content) 102eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi while match: 103eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi # single line comment 104eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi content = content[:match.start()] + content[match.end():] 105eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi match = JSON_COMMENTS_RE.search(content) 106eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 1070759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino # Allow trailing commas in dicts an lists in JSON 1080759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino # Note that this simple implementation will mangle things like: 1090759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino # {"config": ", }"} 1100759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino content = re.sub(r',[ \t\r\n]+}', '}', content) 1110759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino content = re.sub(r',[ \t\r\n]+\]', ']', content) 1120759fbf90dfcf666d32e48f63a06871956bddd0cJavi Merino 113eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi # Return json file 114eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi self.json = json.loads(content, parse_int=int) 115c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi self._log.debug('Loaded JSON configuration:') 116c278c2343c7f25083a80cb164b6bdc761d50050bPatrick Bellasi self._log.debug(' %s', self.json) 117eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 118eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi return self.json 119eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 120eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi def show(self): 12104cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman """ 12204cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman Pretty-print content of parsed JSON 12304cf98b450f336d0e54c0b98adc9305e1a0647f9Brendan Jackman """ 124eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi print json.dumps(self.json, indent=4) 125eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 126eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi# Regular expression for comments 127eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick BellasiJSON_COMMENTS_RE = re.compile( 12827683041209d4cf7bd5276e82a1622d6b3b01c14Javi Merino r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?', 129eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi re.DOTALL | re.MULTILINE 130eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi) 131eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 132eb467dfa2d71d93ef7c5fc2b7562a3f2286d89cdPatrick Bellasi 133