Correct handling of boolean selections
The selections proxy class, which mashes together command-line arguments and configuration options, contained a longstanding and fatal flaw with its handling of boolean values. In particular, falsey values were consistently treated as truthy due to naively recasting str to bool (which will always yield True unless empty). This went unnoticed for so long because the majority of these settings default to False, meaning the only reason most users had to set them was to override them to True. Many thanks to Jordan Russell for bringing this bug to my attention, and for supplying an initial patch on which this fix is heavily based. Co-Authored-By: Jordan Russell
This commit is contained in:
34
weather.py
34
weather.py
@@ -95,7 +95,19 @@ class Selections:
|
|||||||
return None
|
return None
|
||||||
def get_bool(self, option, argument=None):
|
def get_bool(self, option, argument=None):
|
||||||
"""Get data and coerce to a boolean if necessary."""
|
"""Get data and coerce to a boolean if necessary."""
|
||||||
return bool(self.get(option, argument))
|
# Mimic configparser's getboolean() method by treating
|
||||||
|
# false/no/off/0 as False and true/yes/on/1 as True values,
|
||||||
|
# case-insensitively
|
||||||
|
value = self.get(option, argument)
|
||||||
|
if isinstance(value, bool):
|
||||||
|
return value
|
||||||
|
if isinstance(value, str):
|
||||||
|
vlower = value.lower()
|
||||||
|
if vlower in ('false', 'no', 'off', '0'):
|
||||||
|
return False
|
||||||
|
elif vlower in ('true', 'yes', 'on', '1'):
|
||||||
|
return True
|
||||||
|
raise ValueError("Not a boolean: %s" % value)
|
||||||
def getint(self, option, argument=None):
|
def getint(self, option, argument=None):
|
||||||
"""Get data and coerce to an integer if necessary."""
|
"""Get data and coerce to an integer if necessary."""
|
||||||
value = self.get(option, argument)
|
value = self.get(option, argument)
|
||||||
@@ -365,7 +377,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the -a/--alert option
|
# the -a/--alert option
|
||||||
if config.has_option("default", "alert"):
|
if config.has_option("default", "alert"):
|
||||||
default_alert = bool(config.get("default", "alert"))
|
default_alert = config.getboolean("default", "alert")
|
||||||
else: default_alert = False
|
else: default_alert = False
|
||||||
option_parser.add_option("-a", "--alert",
|
option_parser.add_option("-a", "--alert",
|
||||||
dest="alert",
|
dest="alert",
|
||||||
@@ -420,7 +432,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the -f/--forecast option
|
# the -f/--forecast option
|
||||||
if config.has_option("default", "forecast"):
|
if config.has_option("default", "forecast"):
|
||||||
default_forecast = bool(config.get("default", "forecast"))
|
default_forecast = config.getboolean("default", "forecast")
|
||||||
else: default_forecast = False
|
else: default_forecast = False
|
||||||
option_parser.add_option("-f", "--forecast",
|
option_parser.add_option("-f", "--forecast",
|
||||||
dest="forecast",
|
dest="forecast",
|
||||||
@@ -448,7 +460,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the --imperial option
|
# the --imperial option
|
||||||
if config.has_option("default", "imperial"):
|
if config.has_option("default", "imperial"):
|
||||||
default_imperial = bool(config.get("default", "imperial"))
|
default_imperial = config.getboolean("default", "imperial")
|
||||||
else: default_imperial = False
|
else: default_imperial = False
|
||||||
option_parser.add_option("--imperial",
|
option_parser.add_option("--imperial",
|
||||||
dest="imperial",
|
dest="imperial",
|
||||||
@@ -479,7 +491,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the -m/--metric option
|
# the -m/--metric option
|
||||||
if config.has_option("default", "metric"):
|
if config.has_option("default", "metric"):
|
||||||
default_metric = bool(config.get("default", "metric"))
|
default_metric = config.getboolean("default", "metric")
|
||||||
else: default_metric = False
|
else: default_metric = False
|
||||||
option_parser.add_option("-m", "--metric",
|
option_parser.add_option("-m", "--metric",
|
||||||
dest="metric",
|
dest="metric",
|
||||||
@@ -489,7 +501,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the -n/--no-conditions option
|
# the -n/--no-conditions option
|
||||||
if config.has_option("default", "conditions"):
|
if config.has_option("default", "conditions"):
|
||||||
default_conditions = bool(config.get("default", "conditions"))
|
default_conditions = config.getboolean("default", "conditions")
|
||||||
else: default_conditions = True
|
else: default_conditions = True
|
||||||
option_parser.add_option("-n", "--no-conditions",
|
option_parser.add_option("-n", "--no-conditions",
|
||||||
dest="conditions",
|
dest="conditions",
|
||||||
@@ -499,7 +511,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the --no-cache option
|
# the --no-cache option
|
||||||
if config.has_option("default", "cache"):
|
if config.has_option("default", "cache"):
|
||||||
default_cache = bool(config.get("default", "cache"))
|
default_cache = config.getboolean("default", "cache")
|
||||||
else: default_cache = True
|
else: default_cache = True
|
||||||
option_parser.add_option("--no-cache",
|
option_parser.add_option("--no-cache",
|
||||||
dest="cache",
|
dest="cache",
|
||||||
@@ -509,7 +521,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the --no-cache-data option
|
# the --no-cache-data option
|
||||||
if config.has_option("default", "cache_data"):
|
if config.has_option("default", "cache_data"):
|
||||||
default_cache_data = bool(config.get("default", "cache_data"))
|
default_cache_data = config.getboolean("default", "cache_data")
|
||||||
else: default_cache_data = True
|
else: default_cache_data = True
|
||||||
option_parser.add_option("--no-cache-data",
|
option_parser.add_option("--no-cache-data",
|
||||||
dest="cache_data",
|
dest="cache_data",
|
||||||
@@ -519,7 +531,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the --no-cache-search option
|
# the --no-cache-search option
|
||||||
if config.has_option("default", "cache_search"):
|
if config.has_option("default", "cache_search"):
|
||||||
default_cache_search = bool(config.get("default", "cache_search"))
|
default_cache_search = config.getboolean("default", "cache_search")
|
||||||
else: default_cache_search = True
|
else: default_cache_search = True
|
||||||
option_parser.add_option("--no-cache-search",
|
option_parser.add_option("--no-cache-search",
|
||||||
dest="cache_search",
|
dest="cache_search",
|
||||||
@@ -529,7 +541,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the -q/--quiet option
|
# the -q/--quiet option
|
||||||
if config.has_option("default", "quiet"):
|
if config.has_option("default", "quiet"):
|
||||||
default_quiet = bool(config.get("default", "quiet"))
|
default_quiet = config.getboolean("default", "quiet")
|
||||||
else: default_quiet = False
|
else: default_quiet = False
|
||||||
option_parser.add_option("-q", "--quiet",
|
option_parser.add_option("-q", "--quiet",
|
||||||
dest="quiet",
|
dest="quiet",
|
||||||
@@ -548,7 +560,7 @@ def get_options(config):
|
|||||||
|
|
||||||
# the -v/--verbose option
|
# the -v/--verbose option
|
||||||
if config.has_option("default", "verbose"):
|
if config.has_option("default", "verbose"):
|
||||||
default_verbose = bool(config.get("default", "verbose"))
|
default_verbose = config.getboolean("default", "verbose")
|
||||||
else: default_verbose = False
|
else: default_verbose = False
|
||||||
option_parser.add_option("-v", "--verbose",
|
option_parser.add_option("-v", "--verbose",
|
||||||
dest="verbose",
|
dest="verbose",
|
||||||
|
|||||||
Reference in New Issue
Block a user