a904bd30f4097595c11c224a817ddfadad732faf
[tiramisu.git] / tiramisu / error.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012-2017 Team tiramisu (see AUTHORS for all contributors)
3 #
4 # This program is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Lesser General Public License as published by the
6 # Free Software Foundation, either version 3 of the License, or (at your
7 # option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful, but WITHOUT
10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
12 # details.
13 #
14 # You should have received a copy of the GNU Lesser General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 # ____________________________________________________________
17 "user defined exceptions"
18 from .i18n import _
19
20
21 def display_list(lst, separator='and'):
22     if separator == 'and':
23         separator = _('and')
24     elif separator == 'or':
25         separator = _('or')
26     if len(lst) == 0:
27         return ''
28     elif len(lst) == 1:
29         ret = lst[0]
30         if isinstance(ret, unicode):
31             ret = ret.encode('utf8')
32         if not isinstance(ret, str):
33             ret = str(ret)
34         return ret
35     else:
36         lst_ = []
37         for l in lst[:-1]:
38             if isinstance(l, unicode):
39                 l = l.encode('utf8')
40             elif not isinstance(l, str):
41                 l = str(l)
42             lst_.append(l)
43         last = lst[-1]
44         if isinstance(last, unicode):
45             last = last.encode('utf8')
46         if not isinstance(last, str):
47             last = str(last)
48         return ', '.join(lst_) + _(' {} ').format(separator) + last
49
50
51 # Exceptions for an Option
52 class PropertiesOptionError(AttributeError):
53     "attempt to access to an option with a property that is not allowed"
54     def __init__(self, msg, proptype, settings=None, datas=None, option_type=None):
55         self.proptype = proptype
56         self._settings = settings
57         self._datas = datas
58         self._type = option_type
59         self._orig_opt = None
60         super(PropertiesOptionError, self).__init__(msg)
61
62     def set_orig_opt(self, opt):
63         self._orig_opt = opt
64
65     def __str__(self):
66         #this part is a bit slow, so only execute when display
67         if self._settings is None:
68             req = {}
69         else:
70             req = self._settings.apply_requires(**self._datas)
71         if req != {} or self._orig_opt is not None:
72             if req != {}:
73                 only_one = len(req) == 1
74                 msg = []
75                 for action, msg_ in req.items():
76                     msg.append('{0} ({1})'.format(action, display_list(msg_)))
77             else:
78                 only_one = len(self.proptype) == 1
79                 msg = self.proptype
80             if only_one:
81                 prop_msg = _('property')
82             else:
83                 prop_msg = _('properties')
84             msg = display_list(msg)
85             if self._orig_opt:
86                 return str(_('cannot access to {0} "{1}" because "{2}" has {3} {4}').format(self._type,
87                                                                                       self._orig_opt.impl_get_display_name(),
88                                                                                       self._datas['opt'].impl_get_display_name(),
89                                                                                       prop_msg,
90                                                                                       msg))
91             else:
92                 return str(_('cannot access to {0} "{1}" because has {2} {3}').format(self._type, self._datas['opt'].impl_get_display_name(), prop_msg, msg))
93         else:
94             return super(PropertiesOptionError, self).__str__()
95
96
97 #____________________________________________________________
98 # Exceptions for a Config
99 class ConfigError(Exception):
100     """attempt to change an option's owner without a value
101     or in case of `_cfgimpl_descr` is None
102     or if a calculation cannot be carried out"""
103     pass
104
105
106 class ConflictError(Exception):
107     "duplicate options are present in a single config"
108     pass
109
110
111 #____________________________________________________________
112 #┬ámiscellaneous exceptions
113 class RequirementError(Exception):
114     """a recursive loop occurs in the requirements tree
115     requires
116     """
117     pass
118
119
120 class SlaveError(Exception):
121     "problem with a slave's value length"
122     pass
123
124
125 class ConstError(TypeError):
126     "no uniq value in _NameSpace"
127     pass
128
129
130 #Warning
131 class ValueWarning(UserWarning):  # pragma: optional cover
132     """Option could warn user and not raise ValueError.
133
134     Example:
135
136     >>> import warnings
137     >>> from tiramisu.error import ValueWarning
138     >>> from tiramisu.option import StrOption, OptionDescription
139     >>> from tiramisu.config import Config
140     >>> warnings.simplefilter("always", ValueWarning)
141     >>> def a(val):
142     ...  raise ValueError('pouet')
143     ...
144     >>> s=StrOption('s', '', validator=a, warnings_only=True)
145     >>> o=OptionDescription('o', '', [s])
146     >>> c=Config(o)
147     >>> c.s = 'val'
148     StrOption:0: ValueWarning: invalid value val for option s: pouet
149     >>> with warnings.catch_warnings(record=True) as w:
150     ...     c.s = 'val'
151     ...
152     >>> w[0].message.opt == s
153     True
154     >>> print str(w[0].message)
155     invalid value val for option s: pouet
156     """
157     def __init__(self, msg, opt):
158         self.opt = opt
159         super(ValueWarning, self).__init__(msg)