Skip to content

Commit d6d02ca

Browse files
committed
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.
1 parent f067636 commit d6d02ca

File tree

2 files changed

+51
-31
lines changed

2 files changed

+51
-31
lines changed

roundup/configuration.py

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,28 @@ def str2value(self, value):
436436
return _val
437437
raise OptionValueError(self, value, self.class_description)
438438

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+
439461

440462
class MailAddressOption(Option):
441463

@@ -607,6 +629,22 @@ def get(self):
607629
"%s: %s." % (self.name, e.filename, e.args[1]))
608630
return self.str2value(_val)
609631

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()
647+
610648

611649
class WebUrlOption(Option):
612650
"""URL MUST start with http/https scheme and end with '/'"""
@@ -1479,6 +1517,10 @@ class Config:
14791517
# actual name of the config file. set on load.
14801518
filepath = os.path.join(HOME, INI_FILE)
14811519

1520+
# List of option names that need additional validation after
1521+
# all options are loaded.
1522+
option_validators = []
1523+
14821524
def __init__(self, config_path=None, layout=None, settings=None):
14831525
"""Initialize confing instance
14841526
@@ -1552,6 +1594,9 @@ def add_option(self, option):
15521594
for _name in option.aliases:
15531595
self.options[_name] = option
15541596

1597+
if hasattr(option, 'validate'):
1598+
self.option_validators.append(option.name)
1599+
15551600
def update_option(self, name, klass,
15561601
default=NODEFAULT, description=None):
15571602
"""Override behaviour of early created option.
@@ -1965,32 +2010,10 @@ def validator(self, options):
19652010
on each other. E.G. indexer_language can only be
19662011
validated if xapian indexer is used.
19672012
"""
1968-
if options['INDEXER']._value in ("", "xapian"):
1969-
try:
1970-
import xapian
1971-
except ImportError:
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)
19872013

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.")
2014+
for option in self.option_validators:
2015+
# validate() should throw an exception if there is an issue.
2016+
options[option].validate(options)
19942017

19952018
def load(self, home_dir):
19962019
"""Load configuration from path designated by home_dir argument"""

test/test_config.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ def testUnsetMailPassword_with_set_username(self):
441441
# look for 'not defined'
442442
self.assertEqual("not defined", cm.exception.args[1])
443443

444+
444445
def testUnsetMailPassword_with_unset_username(self):
445446
""" Set [mail] username but don't set the
446447
[mail] password. Should get an OptionValueError.
@@ -461,10 +462,8 @@ def testSecretMandatory_missing_file(self):
461462

462463
config = configuration.CoreConfig()
463464

464-
config.load(self.dirname)
465-
466465
with self.assertRaises(configuration.OptionValueError) as cm:
467-
self.assertEqual(config['WEB_SECRET_KEY'],"")
466+
config.load(self.dirname)
468467

469468
print(cm.exception)
470469
self.assertEqual(cm.exception.args[0].setting, "secret_key")
@@ -514,10 +513,8 @@ def testSecretMandatory_empty_file(self):
514513

515514
config = configuration.CoreConfig()
516515

517-
config.load(self.dirname)
518-
519516
with self.assertRaises(configuration.OptionValueError) as cm:
520-
config['WEB_SECRET_KEY']
517+
config.load(self.dirname)
521518

522519
print(cm.exception.args)
523520
self.assertEqual(cm.exception.args[2],"Value must not be empty.")

0 commit comments

Comments
 (0)