add force_permissive in mandatory_warnings
[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 ConfigError(_('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 not self._p_.hasvalue(path):  # 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, force_permissive=False):
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                                               force_permissive=force_permissive)
467                     except PropertiesOptionError as err:
468                         if err.proptype == ['mandatory']:
469                             _ret.append(path)
470             return _ret
471         self.reset_cache(False)
472         descr = self._getcontext().cfgimpl_get_description()
473         ret = _mandatory_warnings(descr)
474         self.reset_cache(False)
475         return ret
476
477     def force_cache(self):
478         """parse all option to force data in cache
479         """
480         context = self.context()
481         if not 'cache' in context.cfgimpl_get_settings():
482             raise ConfigError(_('can force cache only if cache '
483                                 'is actived in config'))
484         #remove all cached properties and value to update "expired" time
485         context.cfgimpl_reset_cache()
486         for path in context.cfgimpl_get_description().impl_getpaths(
487                 include_groups=True):
488             try:
489                 context.getattr(path)
490             except PropertiesOptionError:
491                 pass
492
493     def __getstate__(self):
494         return {'_p_': self._p_}
495
496     def _impl_setstate(self, storage):
497         self._p_._storage = storage
498
499     def __setstate__(self, states):
500         self._p_ = states['_p_']
501
502
503 # ____________________________________________________________
504 # multi types
505
506 class Multi(list):
507     """multi options values container
508     that support item notation for the values of multi options"""
509     __slots__ = ('opt', 'path', 'context', '__weakref__')
510
511     def __init__(self, value, context, opt, path):
512         """
513         :param value: the Multi wraps a list value
514         :param context: the home config that has the values
515         :param opt: the option object that have this Multi value
516         :param path: path of the option
517         """
518         if value is None:
519             value = []
520         if not opt.impl_is_submulti() and isinstance(value, Multi):  # pragma: optional cover
521             raise ValueError(_('{0} is already a Multi ').format(
522                 opt.impl_getname()))
523         self.opt = opt
524         self.path = path
525         if not isinstance(context, weakref.ReferenceType):  # pragma: optional cover
526             raise ValueError('context must be a Weakref')
527         self.context = context
528         if not isinstance(value, list):
529             if not '_index' in self.__slots__ and opt.impl_is_submulti():
530                 value = [[value]]
531             else:
532                 value = [value]
533         elif value != [] and not '_index' in self.__slots__ and \
534                 opt.impl_is_submulti() and not isinstance(value[0], list):
535             value = [value]
536         super(Multi, self).__init__(value)
537         if opt.impl_is_submulti():
538             if not '_index' in self.__slots__:
539                 for idx, val in enumerate(self):
540                     if not isinstance(val, SubMulti):
541                         super(Multi, self).__setitem__(idx, SubMulti(val,
542                                                                      context,
543                                                                      opt, path,
544                                                                      idx))
545                     #FIXME weakref ??
546                     self[idx].submulti = weakref.ref(self)
547
548     def _getcontext(self):
549         """context could be None, we need to test it
550         context is None only if all reference to `Config` object is deleted
551         (for example we delete a `Config` and we manipulate a reference to
552         old `SubConfig`, `Values`, `Multi` or `Settings`)
553         """
554         context = self.context()
555         if context is None:  # pragma: optional cover
556             raise ConfigError(_('the context does not exist anymore'))
557         return context
558
559     def __setitem__(self, index, value):
560         self._setitem(index, value)
561
562     def _setitem(self, index, value):
563         self._validate(value, index, True)
564         #assume not checking mandatory property
565         super(Multi, self).__setitem__(index, value)
566         self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
567                                                           self)
568
569     #def __repr__(self, *args, **kwargs):
570     #    return super(Multi, self).__repr__(*args, **kwargs)
571
572     #def __getitem__(self, y):
573     #    return super(Multi, self).__getitem__(y)
574
575     def _get_validated_value(self, index):
576         values = self._getcontext().cfgimpl_get_values()
577         return values._get_validated_value(self.opt, self.path,
578                                            True, False, None, True,
579                                            index=index)
580
581     def append(self, value=undefined, force=False, setitem=True):
582         """the list value can be updated (appened)
583         only if the option is a master
584         """
585         if not force:
586             if self.opt.impl_is_master_slaves('slave'):  # pragma: optional cover
587                 raise SlaveError(_("cannot append a value on a multi option {0}"
588                                    " which is a slave").format(self.opt.impl_getname()))
589         index = self.__len__()
590         if value is undefined:
591             try:
592                 value = self._get_validated_value(index)
593             except IndexError:
594                 value = None
595         self._validate(value, index, True)
596         if not '_index' in self.__slots__ and self.opt.impl_is_submulti():
597             if not isinstance(value, SubMulti):
598                 value = SubMulti(value, self.context, self.opt, self.path, index)
599             value.submulti = weakref.ref(self)
600         super(Multi, self).append(value)
601         if setitem:
602             self._store(force)
603
604     def sort(self, cmp=None, key=None, reverse=False):
605         if self.opt.impl_is_master_slaves():
606             raise SlaveError(_("cannot sort multi option {0} if master or slave"
607                                "").format(self.opt.impl_getname()))
608         if sys.version_info[0] >= 3:
609             if cmp is not None:
610                 raise ValueError(_('cmp is not permitted in python v3 or '
611                                    'greater'))
612             super(Multi, self).sort(key=key, reverse=reverse)
613         else:
614             super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
615         self._store()
616
617     def reverse(self):
618         if self.opt.impl_is_master_slaves():
619             raise SlaveError(_("cannot reverse multi option {0} if master or "
620                                "slave").format(self.opt.impl_getname()))
621         super(Multi, self).reverse()
622         self._store()
623
624     def insert(self, index, obj):
625         #FIXME obj should be undefined
626         if self.opt.impl_is_master_slaves():
627             raise SlaveError(_("cannot insert multi option {0} if master or "
628                                "slave").format(self.opt.impl_getname()))
629         self._validate(obj, index, True)
630         super(Multi, self).insert(index, obj)
631         self._store()
632
633     def extend(self, iterable):
634         if self.opt.impl_is_master_slaves():
635             raise SlaveError(_("cannot extend multi option {0} if master or "
636                                "slave").format(self.opt.impl_getname()))
637         try:
638             index = self._index
639         except:
640             index = None
641         self._validate(iterable, index)
642         super(Multi, self).extend(iterable)
643         self._store()
644
645     def _validate(self, value, force_index, submulti=False):
646         if value is not None:
647             self.opt.impl_validate(value, context=self._getcontext(),
648                                    force_index=force_index)
649
650     def pop(self, index, force=False):
651         """the list value can be updated (poped)
652         only if the option is a master
653
654         :param index: remove item a index
655         :type index: int
656         :param force: force pop item (withoud check master/slave)
657         :type force: boolean
658         :returns: item at index
659         """
660         context = self._getcontext()
661         if not force:
662             if self.opt.impl_is_master_slaves('slave'):  # pragma: optional cover
663                 raise SlaveError(_("cannot pop a value on a multi option {0}"
664                                    " which is a slave").format(self.opt.impl_getname()))
665             if self.opt.impl_is_master_slaves('master'):
666                 self.opt.impl_get_master_slaves().pop(self.opt,
667                                                       context.cfgimpl_get_values(), index)
668         #set value without valid properties
669         ret = super(Multi, self).pop(index)
670         self._store(force)
671         return ret
672
673     def _store(self, force=False):
674         self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path,
675                                                           self,
676                                                           validate_properties=not force)
677
678
679 class SubMulti(Multi):
680     __slots__ = ('_index', 'submulti')
681
682     def __init__(self, value, context, opt, path, index):
683         """
684         :param index: index (only for slave with submulti)
685         :type index: `int`
686         """
687         self._index = index
688         super(SubMulti, self).__init__(value, context, opt, path)
689
690     def append(self, value=undefined):
691         super(SubMulti, self).append(value, force=True)
692
693     def pop(self, index):
694         return super(SubMulti, self).pop(index, force=True)
695
696     def __setitem__(self, index, value):
697         self._setitem(index, value)
698
699     def _store(self, force=False):
700         #force is unused here
701         self._getcontext().cfgimpl_get_values()._setvalue(self.opt,
702                                                           self.path,
703                                                           self.submulti())
704
705     def _validate(self, value, force_index, submulti=False):
706         if value is not None:
707             if submulti is False:
708                 super(SubMulti, self)._validate(value, force_index)
709             else:
710                 self.opt.impl_validate(value, context=self._getcontext(),
711                                        force_index=self._index,
712                                        force_submulti_index=force_index)
713
714     def _get_validated_value(self, index):
715         values = self._getcontext().cfgimpl_get_values()
716         return values._get_validated_value(self.opt, self.path,
717                                            True, False, None, True,
718                                            index=index,
719                                            submulti_index=self._index)