python 3.5 support
[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 import sys
20
21
22 def display_list(lst, separator='and'):
23     if separator == 'and':
24         separator = _('and')
25     elif separator == 'or':
26         separator = _('or')
27     if len(lst) == 0:
28         return ''
29     elif len(lst) == 1:
30         ret = lst[0]
31         if sys.version_info[0] < 3 and isinstance(ret, unicode):
32             ret = ret.encode('utf8')
33         if not isinstance(ret, str):
34             ret = str(ret)
35         return ret
36     else:
37         lst.sort()
38         lst_ = []
39         for l in lst[:-1]:
40             if sys.version_info[0] < 3 and isinstance(l, unicode):
41                 l = l.encode('utf8')
42             elif not isinstance(l, str):
43                 l = str(l)
44             lst_.append(l)
45         last = lst[-1]
46         if sys.version_info[0] < 3 and isinstance(last, unicode):
47             last = last.encode('utf8')
48         if not isinstance(last, str):
49             last = str(last)
50         return ', '.join(lst_) + _(' {} ').format(separator) + last
51
52
53 # Exceptions for an Option
54 class PropertiesOptionError(AttributeError):
55     "attempt to access to an option with a property that is not allowed"
56     def __init__(self, msg, proptype, settings=None, datas=None, option_type=None):
57         self.proptype = proptype
58         self._settings = settings
59         self._datas = datas
60         self._type = option_type
61         self._orig_opt = None
62         super(PropertiesOptionError, self).__init__(msg)
63
64     def set_orig_opt(self, opt):
65         self._orig_opt = opt
66
67     def __str__(self):
68         #this part is a bit slow, so only execute when display
69         if self._settings is None:
70             req = {}
71         else:
72             req = self._settings.apply_requires(**self._datas)
73         if req != {} or self._orig_opt is not None:
74             if req != {}:
75                 only_one = len(req) == 1
76                 msg = []
77                 for action, msg_ in req.items():
78                     msg.append('{0} ({1})'.format(action, display_list(msg_)))
79             else:
80                 only_one = len(self.proptype) == 1
81                 msg = self.proptype
82             if only_one:
83                 prop_msg = _('property')
84             else:
85                 prop_msg = _('properties')
86             msg = display_list(msg)
87             if self._orig_opt:
88                 return str(_('cannot access to {0} "{1}" because "{2}" has {3} {4}').format(self._type,
89                                                                                       self._orig_opt.impl_get_display_name(),
90                                                                                       self._datas['opt'].impl_get_display_name(),
91                                                                                       prop_msg,
92                                                                                       msg))
93             else:
94                 return str(_('cannot access to {0} "{1}" because has {2} {3}').format(self._type, self._datas['opt'].impl_get_display_name(), prop_msg, msg))
95         else:
96             return super(PropertiesOptionError, self).__str__()
97
98
99 #____________________________________________________________
100 # Exceptions for a Config
101 class ConfigError(Exception):
102     """attempt to change an option's owner without a value
103     or in case of `_cfgimpl_descr` is None
104     or if a calculation cannot be carried out"""
105     pass
106
107
108 class ConflictError(Exception):
109     "duplicate options are present in a single config"
110     pass
111
112
113 #____________________________________________________________
114 #┬ámiscellaneous exceptions
115 class RequirementError(Exception):
116     """a recursive loop occurs in the requirements tree
117     requires
118     """
119     pass
120
121
122 class SlaveError(Exception):
123     "problem with a slave's value length"
124     pass
125
126
127 class ConstError(TypeError):
128     "no uniq value in _NameSpace"
129     pass
130
131
132 #Warning
133 class ValueWarning(UserWarning):  # pragma: optional cover
134     """Option could warn user and not raise ValueError.
135
136     Example:
137
138     >>> import warnings
139     >>> from tiramisu.error import ValueWarning
140     >>> from tiramisu.option import StrOption, OptionDescription
141     >>> from tiramisu.config import Config
142     >>> warnings.simplefilter("always", ValueWarning)
143     >>> def a(val):
144     ...  raise ValueError('pouet')
145     ...
146     >>> s=StrOption('s', '', validator=a, warnings_only=True)
147     >>> o=OptionDescription('o', '', [s])
148     >>> c=Config(o)
149     >>> c.s = 'val'
150     StrOption:0: ValueWarning: invalid value val for option s: pouet
151     >>> with warnings.catch_warnings(record=True) as w:
152     ...     c.s = 'val'
153     ...
154     >>> w[0].message.opt == s
155     True
156     >>> print str(w[0].message)
157     invalid value val for option s: pouet
158     """
159     def __init__(self, msg, opt):
160         self.opt = opt
161         super(ValueWarning, self).__init__(msg)