[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [fallback-scripts/master] script: improve bool env var conversion errors and default handling
commit ba94afcb7aa76b8a759da5109e582584dbe615a5
Author: teor <teor@xxxxxxxxxxxxxx>
Date: Thu Aug 1 18:27:52 2019 +1000
script: improve bool env var conversion errors and default handling
* Restrict the valid True and False bool values
* use default for unset env var, and (not default) for empty set env var
Closes 31305.
---
updateFallbackDirs.py | 81 +++++++++++++++++++++++++++++++++++++++------------
1 file changed, 63 insertions(+), 18 deletions(-)
diff --git a/updateFallbackDirs.py b/updateFallbackDirs.py
index 00e41f8..02f97e3 100755
--- a/updateFallbackDirs.py
+++ b/updateFallbackDirs.py
@@ -73,12 +73,25 @@ except ImportError:
## Top-Level Configuration
-def getenv_conf(var_name, default_val, type_fn):
+def getenv_conf(var_name, default_val, type_fn, optional=False):
"""Get var_name from the environment, using default_val if it is unset.
Cast the result using type_fn. If conversion fails, log an error and
- exit."""
+ exit.
+ type_fn must not be bool. Instead, use custom_bool, which correctly
+ handles empty env vars and defaults, and bad values."""
try:
- return type_fn(os.getenv(var_name, default_val))
+ original_type_fn = type_fn
+ # Use our custom bool function instead
+ assert type_fn != bool
+ # Make the type function optional
+ if optional:
+ type_fn = opt(type_fn)
+ # Look up and convert the value
+ if original_type_fn == custom_bool:
+ # custom_bool does its own default handling
+ return type_fn(os.getenv(var_name), default_val, var_name)
+ else:
+ return type_fn(os.getenv(var_name, default_val))
except ValueError as e:
# Log a useful message if conversion fails
logging.error('Could not cast env var "{}" using function "{}" and default "{}". ValueError: "{}"'.format(var_name, type_fn, default_val, e))
@@ -94,9 +107,15 @@ def opt(type_fn):
If conversion fails, and var_value is None or the empty string, returns
None.
If conversion fails for any other values, throws a ValueError
- exception, with an error string containing var_name, if present."""
+ exception, with an error string containing var_name, if present.
+ Performs special handling for bool conversion using custom_bool()."""
try:
- return type_fn(var_value)
+ if type_fn == custom_bool:
+ assert default_val is not None
+ assert var_name is not None
+ return custom_bool(var_value, default_val, var_name)
+ else:
+ return type_fn(var_value)
# Make type_fn(None) always return None for types that don't cast None
except TypeError:
return None
@@ -111,6 +130,32 @@ def opt(type_fn):
raise e
return opt_type_fn
+# Permitted True and False values for custom_bool(). Must be lowercase.
+CUSTOM_BOOL_TRUE = ['true', 'yes', '1']
+CUSTOM_BOOL_FALSE = ['false', 'no', '0']
+
+def custom_bool(raw_var_value, default_val, var_name=None):
+ """Custom bool conversion function.
+ If raw_var_value is None, returns default_val.
+ If raw_var_value is the empty string, returns not default_val,
+ Otherwise, checks CUSTOM_BOOL_TRUE and CUSTOM_BOOL_FALSE for
+ raw_var_value, returning True or False respectively.
+ Any other raw_var_value throws a ValueError.
+ If var_name is not None, it is included in the ValueError string."""
+ if raw_var_value is None:
+ return default_val
+ elif raw_var_value == '':
+ return not default_val
+ elif str.lower(raw_var_value) in CUSTOM_BOOL_TRUE:
+ return True
+ elif str.lower(raw_var_value) in CUSTOM_BOOL_FALSE:
+ return False
+ else:
+ error_str = "invalid literal for custom_bool(): '{}', default_val: '{}'".format(raw_var_value, default_val)
+ if var_name is not None:
+ error_str += ", var_name: '{}'".format(var_name)
+ raise ValueError(error_str)
+
# We use semantic versioning: https://semver.org
# In particular:
# * major changes include removing a mandatory field, or anything else that
@@ -133,30 +178,30 @@ MODE = getenv_conf('TOR_FB_MODE',
# Output all candidate fallbacks, or only output selected fallbacks?
OUTPUT_CANDIDATES = getenv_conf('TOR_FB_OUTPUT_CANDIDATES',
- False, bool)
+ False, custom_bool)
# Perform DirPort checks over IPv4?
# Change this to False if IPv4 doesn't work for you, or if you don't want to
# download a consensus for each fallback
# Don't check ~1000 candidates when OUTPUT_CANDIDATES is True
PERFORM_IPV4_DIRPORT_CHECKS = getenv_conf('TOR_FB_PERFORM_IPV4_DIRPORT_CHECKS',
- not OUTPUT_CANDIDATES, bool)
+ not OUTPUT_CANDIDATES, custom_bool)
# Perform DirPort checks over IPv6?
# There are no IPv6 DirPorts in the Tor protocol, so we disable this option by
# default. When #18394 is implemented, we'll be able to check IPv6 ORPorts.
PERFORM_IPV6_DIRPORT_CHECKS = getenv_conf('TOR_FB_PERFORM_IPV6_DIRPORT_CHECKS',
- False, bool)
+ False, custom_bool)
# Must relays be running now?
MUST_BE_RUNNING_NOW = getenv_conf('TOR_FB_MUST_BE_RUNNING_NOW',
(PERFORM_IPV4_DIRPORT_CHECKS
- or PERFORM_IPV6_DIRPORT_CHECKS), bool)
+ or PERFORM_IPV6_DIRPORT_CHECKS), custom_bool)
# Clients have been using microdesc consensuses by default for a while now
DOWNLOAD_MICRODESC_CONSENSUS = (
getenv_conf('TOR_FB_DOWNLOAD_MICRODESC_CONSENSUS',
- True, bool))
+ True, custom_bool))
# If a relay delivers an invalid consensus, if it will become valid less than
# this many seconds in the future, or expired less than this many seconds ago,
@@ -185,12 +230,12 @@ REASONABLY_LIVE_TIME = getenv_conf('TOR_FB_REASONABLY_LIVE_TIME',
# Output fallback name, flags, bandwidth, and ContactInfo in a C comment?
OUTPUT_COMMENTS = getenv_conf('TOR_FB_OUTPUT_COMMENTS',
- OUTPUT_CANDIDATES, bool)
+ OUTPUT_CANDIDATES, custom_bool)
# Output matching ContactInfo in fallbacks list?
# Useful if you're trying to contact operators
CONTACT_COUNT = getenv_conf('TOR_FB_CONTACT_COUNT',
- OUTPUT_CANDIDATES, bool)
+ OUTPUT_CANDIDATES, custom_bool)
# How the list should be sorted:
# fingerprint: is useful for stable diffs of fallback lists
@@ -211,12 +256,12 @@ ONIONOO = getenv_conf('TOR_FB_ONIONOO',
# None means "all relays".
# Set env TOR_FB_ONIONOO_LIMIT="None" to request all relays.
ONIONOO_LIMIT = getenv_conf('TOR_FB_ONIONOO_LIMIT',
- None, opt(int))
+ None, int, optional=True)
# Don't bother going out to the Internet, just use the files available locally,
# even if they're very old
LOCAL_FILES_ONLY = getenv_conf('TOR_FB_LOCAL_FILES_ONLY',
- False, bool)
+ False, custom_bool)
## Whitelist Filter Settings
@@ -226,7 +271,7 @@ LOCAL_FILES_ONLY = getenv_conf('TOR_FB_LOCAL_FILES_ONLY',
# What happens to entries not in whitelist?
# When True, they are included, when False, they are excluded
INCLUDE_UNLISTED_ENTRIES = getenv_conf('TOR_FB_INCLUDE_UNLISTED_ENTRIES',
- OUTPUT_CANDIDATES, bool)
+ OUTPUT_CANDIDATES, custom_bool)
WHITELIST_FILE_NAME = getenv_conf('TOR_FB_WHITELIST_FILE_NAME',
'fallback.whitelist', str)
@@ -289,14 +334,14 @@ _FB_POG = 0.2
# Set env TOR_FB_FALLBACK_PROPORTION_OF_GUARDS="None" to have no limit.
FALLBACK_PROPORTION_OF_GUARDS = (
getenv_conf('TOR_FB_FALLBACK_PROPORTION_OF_GUARDS',
- None if OUTPUT_CANDIDATES else _FB_POG, opt(float)))
+ None if OUTPUT_CANDIDATES else _FB_POG, float, optional=True))
# Limit the number of fallbacks (eliminating lowest by advertised bandwidth)
# None means no limit on the number of fallbacks.
# Set env TOR_FB_MAX_FALLBACK_COUNT="None" to have no limit.
MAX_FALLBACK_COUNT = (
getenv_conf('TOR_FB_MAX_FALLBACK_COUNT',
- None if OUTPUT_CANDIDATES else 200, opt(int)))
+ None if OUTPUT_CANDIDATES else 200, int, optional=True))
# Emit a C #error if the number of fallbacks is less than expected
# Set to 0 to have no minimum.
MIN_FALLBACK_COUNT = (
@@ -353,7 +398,7 @@ CONSENSUS_DOWNLOAD_SPEED_MAX = (
# If the relay fails a consensus check, retry the download
# This avoids delisting a relay due to transient network conditions
CONSENSUS_DOWNLOAD_RETRY = getenv_conf('TOR_FB_CONSENSUS_DOWNLOAD_RETRY',
- True, bool)
+ True, custom_bool)
## Parsing Functions
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits