d0a8eaabc1a90c61eecd236dd201fd76b3631ce4
[tiramisu.git] / tiramisu / value.py
1 # -*- coding: utf-8 -*-
2 "takes care of the option's values and multi values"
3 # Copyright (C) 2013-2014 Team tiramisu (see AUTHORS for all contributors)
4 #
5 # This program is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as published by the
7 # Free Software Foundation, either version 3 of the License, or (at your
8 # option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
13 # details.
14 #
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 # ____________________________________________________________
18 from time import time
19 import sys
20 import weakref
21 from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError
22 from tiramisu.setting import owners, expires_time, undefined
23 from tiramisu.autolib import carry_out_calculation
24 from tiramisu.i18n import _
25 from tiramisu.option import SymLinkOption, DynSymLinkOption, Option
26
27
28 class Values(object):
29     """The `Config`'s root is indeed  in charge of the `Option()`'s values,
30     but the values are physicaly located here, in `Values`, wich is also
31     responsible of a caching utility.
32     """
33     __slots__ = ('context', '_p_', '__weakref__')
34
35     def __init__(self, context, storage):
36         """
37         Initializes the values's dict.
38
39         :param context: the context is the home config's values
40
41         """
42         self.context = weakref.ref(context)
43         # the storage type is dictionary or sqlite3
44         self._p_ = storage
45
46     def _getcontext(self):
47         """context could be None, we need to test it
48         context is None only if all reference to `Config` object is deleted
49         (for example we delete a `Config` and we manipulate a reference to
50         old `SubConfig`, `Values`, `Multi` or `Settings`)
51         """
52         context = self.context()
53         if context is None:  # pragma: optional cover
54             raise ConfigError(_('the context does not exist anymore'))
55         return context
56
57     def _getvalue(self, opt, path, is_default, index=undefined):
58         """actually retrieves the value
59
60         :param opt: the `option.Option()` object
61         :returns: the option's value (or the default value if not set)
62         """
63         if opt.impl_is_optiondescription():  # pragma: optional cover
64             raise ValueError(_('optiondescription has no value'))
65         setting = self._getcontext().cfgimpl_get_settings()
66         force_default = 'frozen' in setting._getitem(opt, path) and \
67             'force_default_on_freeze' in setting._getitem(opt, path)
68         if not is_default and not force_default:
69             value = self._p_.getvalue(path)
70             if index is not undefined:
71                 try:
72                     return value[index]
73                 except IndexError:
74                     #value is smaller than expected
75                     #so return default value
76                     pass
77             else:
78                 return value
79         #so default value
80         # if value has callback and is not set
81         if opt.impl_has_callback():
82             callback, callback_params = opt.impl_get_callback()
83             value = carry_out_calculation(opt, config=self._getcontext(),
84                                           callback=callback,
85                                           callback_params=callback_params,
86                                           index=index)
87             try:
88                 if isinstance(value, list) and index is not undefined:
89                     #if submulti and return a list of list, just return list
90                     if opt.impl_is_submulti():
91                         val = value[index]
92                         if isinstance(val, list):
93                             value = val
94                     else:
95                         value = value[index]
96                 return value
97             except IndexError:
98                 pass
99         meta = self._getcontext().cfgimpl_get_meta()
100         if meta is not None:
101             #FIXME : problème de longueur si meta + slave
102             #doit passer de meta à pas meta
103             #en plus il faut gérer la longueur avec les meta !
104             #FIXME SymLinkOption
105             value = meta.cfgimpl_get_values()[opt]
106             if isinstance(value, Multi):
107                 if index is not undefined:
108                     value = value[index]
109                 else:
110                     value = list(value)
111             return value
112         # now try to get default value
113         value = opt.impl_getdefault()
114         if opt.impl_is_multi() and index is not undefined:
115             if value is None:
116                 value = opt.impl_getdefault_multi()
117             else:
118                 try:
119                     value = value[index]
120                 except IndexError:
121                     value = opt.impl_getdefault_multi()
122         return value
123
124     def get_modified_values(self):
125         context = self._getcontext()
126         if context._impl_descr is not None:
127             for opt, path in context.cfgimpl_get_description(
128             )._get_force_store_value():
129                 self._getowner(opt, path, force_permissive=True)
130         return self._p_.get_modified_values()
131
132     def __contains__(self, opt):
133         """
134         implements the 'in' keyword syntax in order provide a pythonic way
135         to kow if an option have a value
136
137         :param opt: the `option.Option()` object
138         """
139         path = opt.impl_getpath(self._getcontext())
140         return self._contains(path)
141
142     def _contains(self, path):
143         return self._p_.hasvalue(path)
144
145     def __delitem__(self, opt):
146         """overrides the builtins `del()` instructions"""
147         self.reset(opt)
148
149     def reset(self, opt, path=None):
150         if path is None:
151             path = opt.impl_getpath(self._getcontext())
152         context = self._getcontext()
153         context.cfgimpl_get_settings().validate_properties(opt, False, True,
154                                                            path)
155         if self._p_.hasvalue(path):
156             setting = context.cfgimpl_get_settings()
157             opt.impl_validate(opt.impl_getdefault(),
158                               context, 'validator' in setting)
159             context.cfgimpl_reset_cache()
160             if opt.impl_is_master_slaves('master'):
161                 opt.impl_get_master_slaves().reset(opt, self)
162             self._p_.resetvalue(path)
163
164     def _isempty(self, opt, value):
165         "convenience method to know if an option is empty"
166         empty = opt._empty
167         if value is not undefined:
168             empty_not_multi = not opt.impl_is_multi() and (value is None or
169                                                            value == empty)
170             empty_multi = opt.impl_is_multi() and (value == [] or
171                                                    None in value or
172                                                    empty in value)
173         else:
174             empty_multi = empty_not_multi = False
175         return empty_not_multi or empty_multi
176
177     def __getitem__(self, opt):
178         "enables us to use the pythonic dictionary-like access to values"
179         return self.getitem(opt)
180
181     def getitem(self, opt, validate=True, force_permissive=False):
182         """
183         """
184         return self._get_cached_item(opt, validate=validate,
185                                      force_permissive=force_permissive)
186
187     def _get_cached_item(self, opt, path=None, validate=True,
188                          force_permissive=False, force_properties=None,
189                          validate_properties=True):
190         if path is None:
191             path = opt.impl_getpath(self._getcontext())
192         ntime = None
193         setting = self._getcontext().cfgimpl_get_settings()
194         if 'cache' in setting and self._p_.hascache(path):
195             if 'expire' in setting:
196                 ntime = int(time())
197             is_cached, value = self._p_.getcache(path, ntime)
198             if is_cached:
199                 if opt.impl_is_multi() and not isinstance(value, Multi):
200                     #load value so don't need to validate if is not a Multi
201                     value = Multi(value, self.context, opt, path)
202                 return value
203         val = self._getitem(opt, path, validate, force_permissive,
204                             force_properties, validate_properties)
205         if 'cache' in setting and validate and validate_properties and \
206                 force_permissive is False and force_properties is None:
207             if 'expire' in setting:
208                 if ntime is None:
209                     ntime = int(time())
210                 ntime = ntime + expires_time
211             self._p_.setcache(path, val, ntime)
212         return val
213
214     def _getitem(self, opt, path, validate, force_permissive, force_properties,
215                  validate_properties):
216         if opt.impl_is_master_slaves():
217             return opt.impl_get_master_slaves().getitem(self, opt, path,
218                                                         validate,
219                                                         force_permissive,
220                                                         force_properties,
221                                                         validate_properties)
222         else:
223             return self._get_validated_value(opt, path, validate,
224                                              force_permissive,
225                                              force_properties,
226                                              validate_properties)
227
228     def _get_validated_value(self, opt, path, validate, force_permissive,
229                              force_properties, validate_properties,
230                              index=undefined, submulti_index=undefined):
231         """same has getitem but don't touch the cache
232         index is None for slave value, if value returned is not a list, just return []
233         """
234         context = self._getcontext()
235         setting = context.cfgimpl_get_settings()
236         is_default = self._is_default_owner(opt, path,
237                                             validate_properties=False,
238                                             validate_meta=False)
239         try:
240             if index is None:
241                 gv_index = undefined
242             else:
243                 gv_index = index
244             value = self._getvalue(opt, path, is_default, index=gv_index)
245             config_error = None
246         except ConfigError as err:
247             # For calculating properties, we need value (ie for mandatory
248             # value).
249             # If value is calculating with a PropertiesOptionError's option
250             # _getvalue raise a ConfigError.
251             # We can not raise ConfigError if this option should raise
252             # PropertiesOptionError too. So we get config_error and raise
253             # ConfigError if properties did not raise.
254             # cannot assign config_err directly in python 3.3
255             config_error = err
256             # value is not set, for 'undefined' (cannot set None because of
257             # mandatory property)
258             value = undefined
259
260         if config_error is None:
261             if index is undefined:
262                 force_index = None
263             else:
264                 force_index = index
265             if opt.impl_is_multi():
266                 #for slave is a multi
267                 if index is None and not isinstance(value, list):
268                     value = []
269                 if force_index is None:
270                     value = Multi(value, self.context, opt, path)
271                 elif opt.impl_is_submulti() and submulti_index is undefined:
272                     value = SubMulti(value, self.context, opt, path,
273                                      force_index)
274
275             if validate:
276                 if submulti_index is undefined:
277                     force_submulti_index = None
278                 else:
279                     force_submulti_index = submulti_index
280                 opt.impl_validate(value, context, 'validator' in setting,
281                                   force_index=force_index,
282                                   force_submulti_index=force_submulti_index)
283             #FIXME pas de test avec les metas ...
284             #FIXME et les symlinkoption ...
285             if is_default and 'force_store_value' in setting._getitem(opt,
286                                                                       path):
287                 if isinstance(value, Multi):
288                     item = list(value)
289                 else:
290                     item = value
291                 self.setitem(opt, item, path, is_write=False,
292                              force_permissive=force_permissive)
293         if validate_properties:
294             setting.validate_properties(opt, False, False, value=value,
295                                         path=path,
296                                         force_permissive=force_permissive,
297                                         force_properties=force_properties)
298         if config_error is not None:
299             raise config_error
300         return value
301
302     def __setitem__(self, opt, value):  # pragma: optional cover
303         raise ValueError(_('you should only set value with config'))
304
305     def setitem(self, opt, value, path, force_permissive=False,
306                 is_write=True):
307         # is_write is, for example, used with "force_store_value"
308         # user didn't change value, so not write
309         # valid opt
310         context = self._getcontext()
311         opt.impl_validate(value, context,
312                           'validator' in context.cfgimpl_get_settings())
313         if opt.impl_is_multi():
314             #value = Multi(value, self.context, opt, path)
315             if opt.impl_is_master_slaves():
316                 opt.impl_get_master_slaves().setitem(self, opt, value, path)
317         self._setvalue(opt, path, value, force_permissive=force_permissive,
318                        is_write=is_write)
319
320     def _setvalue(self, opt, path, value, force_permissive=False,
321                   is_write=True, validate_properties=True):
322         context = self._getcontext()
323         context.cfgimpl_reset_cache()
324         if validate_properties:
325             setting = context.cfgimpl_get_settings()
326             setting.validate_properties(opt, False, is_write,
327                                         value=value, path=path,
328                                         force_permissive=force_permissive)
329         owner = context.cfgimpl_get_settings().getowner()
330         if isinstance(value, Multi):
331             value = list(value)
332             if opt.impl_is_submulti():
333                 for idx, val in enumerate(value):
334                     if isinstance(val, SubMulti):
335                         value[idx] = list(val)
336         self._p_.setvalue(path, value, owner)
337
338     def getowner(self, opt, force_permissive=False):
339         """
340         retrieves the option's owner
341
342         :param opt: the `option.Option` object
343         :param force_permissive: behaves as if the permissive property
344                                  was present
345         :returns: a `setting.owners.Owner` object
346         """
347         if isinstance(opt, SymLinkOption) and \
348                 not isinstance(opt, DynSymLinkOption):
349             opt = opt._opt
350         path = opt.impl_getpath(self._getcontext())
351         return self._getowner(opt, path, force_permissive=force_permissive)
352
353     def _getowner(self, opt, path, validate_properties=True,
354                   force_permissive=False, validate_meta=True):
355         if not isinstance(opt, Option) and not isinstance(opt, DynSymLinkOption):
356             raise ConfigError(_('owner only avalaible for an option'))
357         context = self._getcontext()
358         setting = context.cfgimpl_get_settings()
359         if 'frozen' in setting[opt] and \
360                 'force_default_on_freeze' in setting[opt]:
361             return owners.default
362         if validate_properties:
363             self._getitem(opt, path, True, force_permissive, None, True)
364         owner = self._p_.getowner(path, owners.default)
365         if validate_meta:
366             meta = context.cfgimpl_get_meta()
367             if owner is owners.default and meta is not None:
368                 owner = meta.cfgimpl_get_values()._getowner(opt, path)
369         return owner
370
371     def setowner(self, opt, owner):
372         """
373         sets a owner to an option
374
375         :param opt: the `option.Option` object
376         :param owner: a valid owner, that is a `setting.owners.Owner` object
377         """
378         if not isinstance(owner, owners.Owner):  # pragma: optional cover
379             raise TypeError(_("invalid generic owner {0}").format(str(owner)))
380
381         path = opt.impl_getpath(self._getcontext())
382         self._setowner(opt, path, owner)
383
384     def _setowner(self, opt, path, owner):
385         if self._getowner(opt, path) == owners.default:  # pragma: optional cover
386             raise ConfigError(_('no value for {0} cannot change owner to {1}'
387                                 '').format(path, owner))
388         self._getcontext().cfgimpl_get_settings().validate_properties(opt,
389                                                                       False,
390                                                                       True,
391                                                                       path)
392
393         self._p_.setowner(path, owner)
394
395     def is_default_owner(self, opt, validate_properties=True,
396                          validate_meta=True):
397         """
398         :param config: *must* be only the **parent** config
399                        (not the toplevel config)
400         :return: boolean
401         """
402         path = opt.impl_getpath(self._getcontext())
403         return self._is_default_owner(opt, path,
404                                       validate_properties=validate_properties,
405                                       validate_meta=validate_meta)
406
407     def _is_default_owner(self, opt, path, validate_properties=True,
408                           validate_meta=True):
409         return self._getowner(opt, path, validate_properties,
410                               validate_meta=validate_meta) == owners.default
411
412     def reset_cache(self, only_expired):
413         """
414         clears the cache if necessary
415         """
416         if only_expired:
417             self._p_.reset_expired_cache(int(time()))
418         else:
419             self._p_.reset_all_cache()
420
421     # information
422     def set_information(self, key, value):
423         """updates the information's attribute
424
425         :param key: information's key (ex: "help", "doc"
426         :param value: information's value (ex: "the help string")
427         """
428         self._p_.set_information(key, value)
429
430     def get_information(self, key, default=undefined):
431         """retrieves one information's item
432
433         :param key: the item string (ex: "help")
434         """
435         try:
436             return self._p_.get_information(key)
437         except ValueError:  # pragma: optional cover
438             if default is not undefined:
439                 return default
440             else:
441                 raise ValueError(_("information's item"
442                                    " not found: {0}").format(key))
443
444     def mandatory_warnings(self):
445         """convenience function to trace Options that are mandatory and
446         where no value has been set
447
448         :returns: generator of mandatory Option's path
449
450         """
451         def _mandatory_warnings(description):
452             #if value in cache, properties are not calculated
453             _ret = []
454             for opt in description._impl_getchildren(
455                     context=self._getcontext()):
456                 if opt.impl_is_optiondescription():
457                     _ret.extend(_mandatory_warnings(opt))
458                 elif isinstance(opt, SymLinkOption) and \
459                         not isinstance(opt, DynSymLinkOption):
460                     pass
461                 else:
462                     path = opt.impl_getpath(self._getcontext())
463                     try:
464                         self._get_cached_item(opt, path=path,
465                                               force_properties=frozenset(('mandatory',)))
466                     except PropertiesOptionError as err:
467                         if err.proptype == ['mandatory']:
468                             _ret.append(path)
469             return _ret
470         self.reset_cache(False)
471         descr = self._getcontext().cfgimpl_get_description()
472         ret = _mandatory_warnings(descr)
473         self.reset_cache(False)
474         return ret
475
476     def force_cache(self):
477         """parse all option to force data in cache
478         """
479         context = self.context()
480         if not 'cache' in context.cfgimpl_get_settings():
481             raise ConfigError(_('can force cache only if cache '
482                                 'is actived in config'))
483         #remove all cached properties and value to update "expired" time
484         context.cfgimpl_reset_cache()
485         for path in context.cfgimpl_get_description().impl_getpaths(
486                 include_groups=True):
487             try:
488                 context.getattr(path)
489             except PropertiesOptionError:
490                 pass
491
492     def __getstate__(self):
493         return {'_p_': self._p_}
494
495     def _impl_setstate(self, storage):
496         self._p_._storage = storage
497
498     def __setstate__(self, states):
499         self._p_ = states['_p_']
500
501
502 # ____________________________________________________________
503 # multi types
504
505 class Multi(list):
506     """multi options values container
507     that support item notation for the values of multi options"""
508     __slots__ = ('opt', 'path', 'context', '__weakref__')
509
510     def __init__(self, value, context, opt, path):
511         """
512         :param value: the Multi wraps a list value
513         :param context: the home config that has the values
514         :param opt: the option object that have this Multi value
515         :param path: path of the option
516         """
517         if value is None:
518             value = []
519         if not opt.impl_is_submulti() and isinstance(value, Multi):  # pragma: optional cover
520             raise ValueError(_('{0} is already a Multi ').format(
521                 opt.impl_getname()))
522         self.opt = opt
523         self.path = path
524         if not isinstance(context, weakref.ReferenceType):  # pragma: optional cover
525             raise ValueError('context must be a Weakref')
526         self.context = context
527         if not isinstance(value, list):
528             if not '_index' in self.__slots__ and opt.impl_is_submulti():
529                 value = [[value]]
530             else:
531                 value = [value]
532         elif value != [] and not '_index' in self.__slots__ and \
533                 opt.impl_is_submulti() and not isinstance(value[0], list):
534             value = [value]
535         super(Multi, self).__init__(value)
536         if opt.impl_is_submulti():
537             if not '_index' in self.__slots__:
538                 for idx, val in enumerate(self):
539                     if not isinstance(val, SubMulti):
540                         super(Multi, self).__setitem__(idx, SubMulti(val,
541                                                                      context,
542                                                                      opt, path,
543                                                                      idx))
544                     #FIXME weakref ??
545                     self[idx].submulti = weakref.ref(self)
546
547     def _getcontext(self):
548         """context could be None, we need to test it
549         context is None only if all reference to `Config` object is deleted
550         (for example we delete a `Config` and we manipulate a reference to
551         old `SubConfig`, `Values`, `Multi` or `Settings`)
552         """
553         context = self.context()
554         if context is None:  # pragma: optional cover
555             raise ConfigError(_('the context does not exist anymore'))
556         return context
557
558     def __setitem__(self, index, value):
559         self._setitem(index, value)
560
561     def _setitem(self, index, value):
562         self._validate(value, index, True)
563         #assume not checking mandatory property
564         super(Multi, self).__setitem__(index, value)
565         self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
566                                                           self)
567
568     #def __repr__(self, *args, **kwargs):
569     #    return super(Multi, self).__repr__(*args, **kwargs)
570
571     #def __getitem__(self, y):
572     #    return super(Multi, self).__getitem__(y)
573
574     def _get_validated_value(self, index):
575         values = self._getcontext().cfgimpl_get_values()
576         return values._get_validated_value(self.opt, self.path,
577                                            True, False, None, True,
578                                            index=index)
579
580     def append(self, value=undefined, force=False, setitem=True):
581         """the list value can be updated (appened)
582         only if the option is a master
583         """
584         if not force:
585             if self.opt.impl_is_master_slaves('slave'):  # pragma: optional cover
586                 raise SlaveError(_("cannot append a value on a multi option {0}"
587                                    " which is a slave").format(self.opt.impl_getname()))
588         index = self.__len__()
589         if value is undefined:
590             try:
591                 value = self._get_validated_value(index)
592             except IndexError:
593                 value = None
594         self._validate(value, index, True)
595         if not '_index' in self.__slots__ and self.opt.impl_is_submulti():
596             if not isinstance(value, SubMulti):
597                 value = SubMulti(value, self.context, self.opt, self.path, index)
598             value.submulti = weakref.ref(self)
599         super(Multi, self).append(value)
600         if setitem:
601             self._store(force)
602
603     def sort(self, cmp=None, key=None, reverse=False):
604         if self.opt.impl_is_master_slaves():
605             raise SlaveError(_("cannot sort multi option {0} if master or slave"
606                                "").format(self.opt.impl_getname()))
607         if sys.version_info[0] >= 3:
608             if cmp is not None:
609                 raise ValueError(_('cmp is not permitted in python v3 or '
610                                    'greater'))
611             super(Multi, self).sort(key=key, reverse=reverse)
612         else:
613             super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
614         self._store()
615
616     def reverse(self):
617         if self.opt.impl_is_master_slaves():
618             raise SlaveError(_("cannot reverse multi option {0} if master or "
619                                "slave").format(self.opt.impl_getname()))
620         super(Multi, self).reverse()
621         self._store()
622
623     def insert(self, index, obj):
624         #FIXME obj should be undefined
625         if self.opt.impl_is_master_slaves():
626             raise SlaveError(_("cannot insert multi option {0} if master or "
627                                "slave").format(self.opt.impl_getname()))
628         self._validate(obj, index, True)
629         super(Multi, self).insert(index, obj)
630         self._store()
631
632     def extend(self, iterable):
633         if self.opt.impl_is_master_slaves():
634             raise SlaveError(_("cannot extend multi option {0} if master or "
635                                "slave").format(self.opt.impl_getname()))
636         try:
637             index = self._index
638         except:
639             index = None
640         self._validate(iterable, index)
641         super(Multi, self).extend(iterable)
642         self._store()
643
644     def _validate(self, value, force_index, submulti=False):
645         if value is not None:
646             self.opt.impl_validate(value, context=self._getcontext(),
647                                    force_index=force_index)
648
649     def pop(self, index, force=False):
650         """the list value can be updated (poped)
651         only if the option is a master
652
653         :param index: remove item a index
654         :type index: int
655         :param force: force pop item (withoud check master/slave)
656         :type force: boolean
657         :returns: item at index
658         """
659         context = self._getcontext()
660         if not force:
661             if self.opt.impl_is_master_slaves('slave'):  # pragma: optional cover
662                 raise SlaveError(_("cannot pop a value on a multi option {0}"
663                                    " which is a slave").format(self.opt.impl_getname()))
664             if self.opt.impl_is_master_slaves('master'):
665                 self.opt.impl_get_master_slaves().pop(self.opt,
666                                                       context.cfgimpl_get_values(), index)
667         #set value without valid properties
668         ret = super(Multi, self).pop(index)
669         self._store(force)
670         return ret
671
672     def _store(self, force=False):
673         self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
674                                                           self,
675                                                           validate_properties=not force)
676
677
678 class SubMulti(Multi):
679     __slots__ = ('_index', 'submulti')
680
681     def __init__(self, value, context, opt, path, index):
682         """
683         :param index: index (only for slave with submulti)
684         :type index: `int`
685         """
686         self._index = index
687         super(SubMulti, self).__init__(value, context, opt, path)
688
689     def append(self, value=undefined):
690         super(SubMulti, self).append(value, force=True)
691
692     def pop(self, index):
693         return super(SubMulti, self).pop(index, force=True)
694
695     def __setitem__(self, index, value):
696         self._setitem(index, value)
697
698     def _store(self, force=False):
699         #force is unused here
700         self._getcontext().cfgimpl_get_values()._setvalue(self.opt,
701                                                           self.path,
702                                                           self.submulti())
703
704     def _validate(self, value, force_index, submulti=False):
705         if value is not None:
706             if submulti is False:
707                 super(SubMulti, self)._validate(value, force_index)
708             else:
709                 self.opt.impl_validate(value, context=self._getcontext(),
710                                        force_index=self._index,
711                                        force_submulti_index=force_index)
712
713     def _get_validated_value(self, index):
714         values = self._getcontext().cfgimpl_get_values()
715         return values._get_validated_value(self.opt, self.path,
716                                            True, False, None, True,
717                                            index=index,
718                                            submulti_index=self._index)