comparison roundup/configuration.py @ 6584:770503bd211e

Validate SecretOption and support validate method Needed to validate SecretOption and verify that file is readable and valid. validator() now calls the validate method for each Option subclass. To add post config load validation, just define a method validate(self, options) for the Option subclass.
author John Rouillard <rouilj@ieee.org>
date Thu, 06 Jan 2022 21:22:26 -0500
parents b1f1539c6a31
children 91ab3e0ffcd0
comparison
equal deleted inserted replaced
6583:3759893f0686 6584:770503bd211e
434 _val = value.lower() 434 _val = value.lower()
435 if _val in self.allowed: 435 if _val in self.allowed:
436 return _val 436 return _val
437 raise OptionValueError(self, value, self.class_description) 437 raise OptionValueError(self, value, self.class_description)
438 438
439 def validate(self, options):
440
441 if self._value in ("", "xapian"):
442 try:
443 import xapian
444 except ImportError:
445 # indexer is probably '' and xapian isn't present
446 # so just return at end of method
447 pass
448 else:
449 try:
450 lang = options["INDEXER_LANGUAGE"]._value
451 xapian.Stem(lang)
452 except xapian.InvalidArgumentError:
453 import textwrap
454 lang_avail = b2s(xapian.Stem.get_available_languages())
455 languages = textwrap.fill(_("Valid languages: ") +
456 lang_avail, 75,
457 subsequent_indent=" ")
458 raise OptionValueError(options["INDEXER_LANGUAGE"],
459 lang, languages)
460
439 461
440 class MailAddressOption(Option): 462 class MailAddressOption(Option):
441 463
442 """Email address 464 """Email address
443 465
604 else: 626 else:
605 raise OptionValueError(self, _val, 627 raise OptionValueError(self, _val,
606 "Unable to read value for %s. Error opening " 628 "Unable to read value for %s. Error opening "
607 "%s: %s." % (self.name, e.filename, e.args[1])) 629 "%s: %s." % (self.name, e.filename, e.args[1]))
608 return self.str2value(_val) 630 return self.str2value(_val)
631
632 def validate(self, options):
633 if self.name == 'MAIL_PASSWORD':
634 if options['MAIL_USERNAME']._value:
635 # MAIL_PASSWORD is an exception. It is mandatory only
636 # if MAIL_USERNAME is set. So check only if username
637 # is set.
638 try:
639 self.get()
640 except OptionUnsetError:
641 # provide error message with link to MAIL_USERNAME
642 raise OptionValueError(options["MAIL_PASSWORD"],
643 "not defined",
644 "Mail username is set, so this must be defined.")
645 else:
646 self.get()
609 647
610 648
611 class WebUrlOption(Option): 649 class WebUrlOption(Option):
612 """URL MUST start with http/https scheme and end with '/'""" 650 """URL MUST start with http/https scheme and end with '/'"""
613 651
1477 # mapping from option names and aliases to Option instances 1515 # mapping from option names and aliases to Option instances
1478 options = None 1516 options = None
1479 # actual name of the config file. set on load. 1517 # actual name of the config file. set on load.
1480 filepath = os.path.join(HOME, INI_FILE) 1518 filepath = os.path.join(HOME, INI_FILE)
1481 1519
1520 # List of option names that need additional validation after
1521 # all options are loaded.
1522 option_validators = []
1523
1482 def __init__(self, config_path=None, layout=None, settings=None): 1524 def __init__(self, config_path=None, layout=None, settings=None):
1483 """Initialize confing instance 1525 """Initialize confing instance
1484 1526
1485 Parameters: 1527 Parameters:
1486 config_path: 1528 config_path:
1549 # (section, name) key is used for writing .ini file 1591 # (section, name) key is used for writing .ini file
1550 self.options[(_section, _name)] = option 1592 self.options[(_section, _name)] = option
1551 # make the option known under all of its A.K.A.s 1593 # make the option known under all of its A.K.A.s
1552 for _name in option.aliases: 1594 for _name in option.aliases:
1553 self.options[_name] = option 1595 self.options[_name] = option
1596
1597 if hasattr(option, 'validate'):
1598 self.option_validators.append(option.name)
1554 1599
1555 def update_option(self, name, klass, 1600 def update_option(self, name, klass,
1556 default=NODEFAULT, description=None): 1601 default=NODEFAULT, description=None):
1557 """Override behaviour of early created option. 1602 """Override behaviour of early created option.
1558 1603
1963 2008
1964 Used to validate settings when options are dependent 2009 Used to validate settings when options are dependent
1965 on each other. E.G. indexer_language can only be 2010 on each other. E.G. indexer_language can only be
1966 validated if xapian indexer is used. 2011 validated if xapian indexer is used.
1967 """ 2012 """
1968 if options['INDEXER']._value in ("", "xapian"): 2013
1969 try: 2014 for option in self.option_validators:
1970 import xapian 2015 # validate() should throw an exception if there is an issue.
1971 except ImportError: 2016 options[option].validate(options)
1972 # indexer is probably '' and xapian isn't present
1973 # so just return at end of method
1974 pass
1975 else:
1976 try:
1977 lang = options["INDEXER_LANGUAGE"]._value
1978 xapian.Stem(lang)
1979 except xapian.InvalidArgumentError:
1980 import textwrap
1981 lang_avail = b2s(xapian.Stem.get_available_languages())
1982 languages = textwrap.fill(_("Valid languages: ") +
1983 lang_avail, 75,
1984 subsequent_indent=" ")
1985 raise OptionValueError(options["INDEXER_LANGUAGE"],
1986 lang, languages)
1987
1988 if options['MAIL_USERNAME']._value != "":
1989 # require password to be set
1990 if options['MAIL_PASSWORD']._value is NODEFAULT:
1991 raise OptionValueError(options["MAIL_PASSWORD"],
1992 "not defined",
1993 "mail username is set, so this must be defined.")
1994 2017
1995 def load(self, home_dir): 2018 def load(self, home_dir):
1996 """Load configuration from path designated by home_dir argument""" 2019 """Load configuration from path designated by home_dir argument"""
1997 if os.path.isfile(os.path.join(home_dir, self.INI_FILE)): 2020 if os.path.isfile(os.path.join(home_dir, self.INI_FILE)):
1998 self.load_ini(home_dir) 2021 self.load_ini(home_dir)

Roundup Issue Tracker: http://roundup-tracker.org/