Imported Upstream version 1.39.3
[platform/upstream/gobject-introspection.git] / giscanner / message.py
1 #!/usr/bin/env python
2 # -*- Mode: Python -*-
3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2010 Red Hat, Inc.
5 # Copyright (C) 2010 Johan Dahlin
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 # 02110-1301, USA.
21 #
22
23 import os
24 import sys
25
26 from . import utils
27
28 (WARNING,
29  ERROR,
30  FATAL) = range(3)
31
32
33 class Position(object):
34     """
35     Represents a position in the source file which we
36     want to inform about.
37     """
38
39     __slots__ = ('filename', 'line', 'column')
40
41     def __init__(self, filename=None, line=None, column=None):
42         self.filename = filename
43         self.line = line
44         self.column = column
45
46     def __cmp__(self, other):
47         return cmp((self.filename, self.line, self.column),
48                    (other.filename, other.line, other.column))
49
50     def __repr__(self):
51         return '<Position %s:%d:%d>' % (os.path.basename(self.filename), self.line or -1,
52                                         self.column or -1)
53
54     def format(self, cwd):
55         filename = os.path.realpath(self.filename)
56         cwd = os.path.realpath(cwd)
57         common_prefix = os.path.commonprefix((filename, cwd))
58         if common_prefix:
59             filename = os.path.relpath(filename, common_prefix)
60
61         if self.column is not None:
62             return '%s:%d:%d' % (filename, self.line, self.column)
63         elif self.line is not None:
64             return '%s:%d' % (filename, self.line, )
65         else:
66             return '%s:' % (filename, )
67
68
69 class MessageLogger(object):
70     _instance = None
71
72     def __init__(self, namespace, output=None):
73         if output is None:
74             output = sys.stderr
75         self._cwd = os.getcwd()
76         self._output = output
77         self._namespace = namespace
78         self._enable_warnings = []
79         self._warning_count = 0
80         self._error_count = 0
81
82     @classmethod
83     def get(cls, *args, **kwargs):
84         if cls._instance is None:
85             cls._instance = cls(*args, **kwargs)
86         return cls._instance
87
88     def enable_warnings(self, log_types):
89         self._enable_warnings = log_types
90
91     def get_warning_count(self):
92         return self._warning_count
93
94     def get_error_count(self):
95         return self._error_count
96
97     def log(self, log_type, text, positions=None, prefix=None):
98         """
99         Log a warning, using optional file positioning information.
100         If the warning is related to a ast.Node type, see log_node().
101         """
102         utils.break_on_debug_flag('warning')
103
104         self._warning_count += 1
105
106         if not log_type in self._enable_warnings:
107             return
108
109         if type(positions) == set:
110             positions = list(positions)
111         if isinstance(positions, Position):
112             positions = [positions]
113
114         if not positions:
115             positions = [Position('<unknown>')]
116
117         for position in positions[:-1]:
118             self._output.write("%s:\n" % (position.format(cwd=self._cwd), ))
119         last_position = positions[-1].format(cwd=self._cwd)
120
121         if log_type == WARNING:
122             error_type = "Warning"
123         elif log_type == ERROR:
124             error_type = "Error"
125             self._error_count += 1
126         elif log_type == FATAL:
127             error_type = "Fatal"
128
129         if prefix:
130             text = ('%s: %s: %s: %s: %s\n' % (last_position, error_type,
131                                               self._namespace.name, prefix, text))
132         else:
133             if self._namespace:
134                 text = ('%s: %s: %s: %s\n' % (last_position, error_type,
135                                               self._namespace.name, text))
136             else:
137                 text = ('%s: %s: %s\n' % (last_position, error_type, text))
138
139         self._output.write(text)
140
141         if log_type == FATAL:
142             utils.break_on_debug_flag('fatal')
143             raise SystemExit(text)
144
145     def log_node(self, log_type, node, text, context=None, positions=None):
146         """
147         Log a warning, using information about file positions from
148         the given node.  The optional context argument, if given, should be
149         another ast.Node type which will also be displayed.  If no file position
150         information is available from the node, the position data from the
151         context will be used.
152         """
153         if positions:
154             pass
155         elif getattr(node, 'file_positions', None):
156             positions = node.file_positions
157         elif context and context.file_positions:
158             positions = context.file_positions
159         else:
160             positions = []
161             if not context:
162                 text = "context=%r %s" % (node, text)
163
164         if context:
165             text = "%s: %s" % (getattr(context, 'symbol', context.name), text)
166         elif not positions and hasattr(node, 'name'):
167             text = "(%s)%s: %s" % (node.__class__.__name__, node.name, text)
168
169         self.log(log_type, text, positions)
170
171     def log_symbol(self, log_type, symbol, text):
172         """Log a warning in the context of the given symbol."""
173         self.log(log_type, text, symbol.position,
174                  prefix="symbol=%r" % (symbol.ident, ))
175
176
177 def log_node(log_type, node, text, context=None, positions=None):
178     ml = MessageLogger.get()
179     ml.log_node(log_type, node, text, context=context, positions=positions)
180
181
182 def warn(text, positions=None, prefix=None):
183     ml = MessageLogger.get()
184     ml.log(WARNING, text, positions, prefix)
185
186
187 def warn_node(node, text, context=None, positions=None):
188     log_node(WARNING, node, text, context=context, positions=positions)
189
190
191 def warn_symbol(symbol, text):
192     ml = MessageLogger.get()
193     ml.log_symbol(WARNING, symbol, text)
194
195
196 def error(text, positions=None, prefix=None):
197     ml = MessageLogger.get()
198     ml.log(ERROR, text, positions, prefix)
199
200
201 def fatal(text, positions=None, prefix=None):
202     ml = MessageLogger.get()
203     ml.log(FATAL, text, positions, prefix)