1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (C) 2015, ARM Limited and contributors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import json
19import os
20import re
21import logging
22import logging.config
23
24
25class LisaLogging(object):
26
27    @classmethod
28    def setup(self, filepath='logging.conf', level=logging.INFO):
29        """
30        Initialize logging used for all the LISA modules.
31
32        :param filepath: the relative or absolute path of the logging configuration to use.
33                         Relative path uses the LISA_HOME environment variable
34                         has base folder.
35        :type filepath: str
36
37        :param level: the default log level to enable, INFO by default
38        :type level: logging.<level> or int in [0..50]
39        """
40
41        # Load the specified logfile using an absolute path
42        basepath = os.path.dirname(__file__).replace('/libs/utils', '')
43        filepath = os.path.join(basepath, filepath)
44        if not os.path.exists(filepath):
45            raise ValueError('Logging configuration file not found in: {}'\
46                             .format(filepath))
47        logging.config.fileConfig(filepath)
48        logging.getLogger().setLevel(level)
49
50        logging.info('Using LISA logging configuration:')
51        logging.info('  %s', filepath)
52
53class JsonConf(object):
54    """
55    Class for parsing a JSON superset with comments.
56
57    Simply strips comments and then uses the standard JSON parser.
58
59    :param filename: Path to file to parse
60    :type filename: str
61    """
62
63    def __init__(self, filename):
64        self.filename = filename
65        self.json = None
66
67    def load(self):
68        """
69        Parse a JSON file
70
71        First remove comments and then use the json module package
72        Comments look like :
73
74        ::
75
76            // ...
77
78        or
79
80        ::
81
82            /*
83            ...
84            */
85
86        """
87
88        # Setup logging
89        self._log = logging.getLogger('JsonConf')
90
91        if not os.path.isfile(self.filename):
92            raise RuntimeError(
93                'Missing configuration file: {}'.format(self.filename)
94            )
95        self._log.debug('loading JSON...')
96
97        with open(self.filename) as fh:
98            content = ''.join(fh.readlines())
99
100            ## Looking for comments
101            match = JSON_COMMENTS_RE.search(content)
102            while match:
103                # single line comment
104                content = content[:match.start()] + content[match.end():]
105                match = JSON_COMMENTS_RE.search(content)
106
107            # Allow trailing commas in dicts an lists in JSON
108            # Note that this simple implementation will mangle things like:
109            # {"config": ", }"}
110            content = re.sub(r',[ \t\r\n]+}', '}', content)
111            content = re.sub(r',[ \t\r\n]+\]', ']', content)
112
113            # Return json file
114            self.json = json.loads(content, parse_int=int)
115            self._log.debug('Loaded JSON configuration:')
116            self._log.debug('   %s', self.json)
117
118        return self.json
119
120    def show(self):
121        """
122        Pretty-print content of parsed JSON
123        """
124        print json.dumps(self.json, indent=4)
125
126# Regular expression for comments
127JSON_COMMENTS_RE = re.compile(
128    r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
129    re.DOTALL | re.MULTILINE
130)
131
132
133