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