[AIRFLOW-3742] Respect the `fallback` arg in airflow.configuration.get
diff --git a/airflow/configuration.py b/airflow/configuration.py
index 70203975446b2..1de5c3aeb1354 100644
--- a/airflow/configuration.py
+++ b/airflow/configuration.py
@@ -36,7 +36,7 @@
 import sys
 import warnings
 
-from backports.configparser import ConfigParser
+from backports.configparser import ConfigParser, _UNSET, NoOptionError
 from zope.deprecation import deprecated
 
 from airflow.exceptions import AirflowConfigException
@@ -247,7 +247,7 @@ def get(self, section, key, **kwargs):
                 return option
 
         # ...then the default config
-        if self.airflow_defaults.has_option(section, key):
+        if self.airflow_defaults.has_option(section, key) or 'fallback' in kwargs:
             return expand_env_var(
                 self.airflow_defaults.get(section, key, **kwargs))
 
@@ -291,9 +291,10 @@ def has_option(self, section, option):
         try:
             # Using self.get() to avoid reimplementing the priority order
             # of config variables (env, config, cmd, defaults)
-            self.get(section, option)
+            # UNSET to avoid logging a warning about missing values
+            self.get(section, option, fallback=_UNSET)
             return True
-        except AirflowConfigException:
+        except NoOptionError:
             return False
 
     def remove_option(self, section, option, remove_default=True):
diff --git a/airflow/settings.py b/airflow/settings.py
index 4a5d99d17b279..035b0ff0e80fb 100644
--- a/airflow/settings.py
+++ b/airflow/settings.py
@@ -176,13 +176,10 @@ def configure_orm(disable_connection_pool=False):
         engine_args['pool_size'] = pool_size
         engine_args['pool_recycle'] = pool_recycle
 
-    try:
-        # Allow the user to specify an encoding for their DB otherwise default
-        # to utf-8 so jobs & users with non-latin1 characters can still use
-        # us.
-        engine_args['encoding'] = conf.get('core', 'SQL_ENGINE_ENCODING')
-    except conf.AirflowConfigException:
-        engine_args['encoding'] = 'utf-8'
+    # Allow the user to specify an encoding for their DB otherwise default
+    # to utf-8 so jobs & users with non-latin1 characters can still use
+    # us.
+    engine_args['encoding'] = conf.get('core', 'SQL_ENGINE_ENCODING', fallback='utf-8')
     # For Python2 we get back a newstr and need a str
     engine_args['encoding'] = engine_args['encoding'].__str__()
 
@@ -226,10 +223,7 @@ def configure_adapters():
 
 
 def validate_session():
-    try:
-        worker_precheck = conf.getboolean('core', 'worker_precheck')
-    except conf.AirflowConfigException:
-        worker_precheck = False
+    worker_precheck = conf.getboolean('core', 'worker_precheck', fallback=False)
     if not worker_precheck:
         return True
     else:
diff --git a/tests/cli/test_worker_initialisation.py b/tests/cli/test_worker_initialisation.py
index 477221693abe1..03fb9487fec58 100644
--- a/tests/cli/test_worker_initialisation.py
+++ b/tests/cli/test_worker_initialisation.py
@@ -59,8 +59,10 @@ def test_worker_precheck_exception(self, mock_getboolean):
         Test to check the behaviour of validate_session method
         when worker_precheck is absent in airflow configuration
         """
-        mock_getboolean.side_effect = airflow.configuration.AirflowConfigException
-        self.assertEqual(airflow.settings.validate_session(), True)
+        mock_getboolean.return_value = False
+
+        self.assertTrue(airflow.settings.validate_session())
+        mock_getboolean.assert_called_once_with('core', 'worker_precheck', fallback=False)
 
     @mock.patch('sqlalchemy.orm.session.Session.execute')
     @mock.patch('airflow.configuration.getboolean')
diff --git a/tests/test_configuration.py b/tests/test_configuration.py
index 9f903f58b3599..df1bcae58d6cb 100644
--- a/tests/test_configuration.py
+++ b/tests/test_configuration.py
@@ -98,6 +98,8 @@ def test_env_var_config(self):
         opt = conf.get('testsection', 'testpercent')
         self.assertEqual(opt, 'with%percent')
 
+        self.assertTrue(conf.has_option('testsection', 'testkey'))
+
     def test_conf_as_dict(self):
         cfg_dict = conf.as_dict()
 
@@ -165,6 +167,10 @@ def test_command_config(self):
         self.assertEqual('key4_result', test_conf.get('test', 'key4'))
         self.assertEqual('value6', test_conf.get('another', 'key6'))
 
+        self.assertEqual('hello', test_conf.get('test', 'key1', fallback='fb'))
+        self.assertEqual('value6', test_conf.get('another', 'key6', fallback='fb'))
+        self.assertEqual('fb', test_conf.get('another', 'key7', fallback='fb'))
+
         self.assertTrue(test_conf.has_option('test', 'key1'))
         self.assertTrue(test_conf.has_option('test', 'key2'))
         self.assertTrue(test_conf.has_option('test', 'key3'))

Make sure you have checked _all_ steps below.

### Jira

- [x] https://issues.apache.org/jira/browse/AIRFLOW-3742


### Description

- [x] This `fallback` argument is part of the API from our parent class, but we didn't
support it because of the various steps we perform in `get()` - this
makes it behave more like the parent class, and can simplify a few
instances in our code (I've only included one that I found here)

### Tests

- [x] My PR adds the following unit tests __OR__ does not need testing for this extremely good reason: few small tests added

### Commits

- [x] My commits all reference Jira issues in their subject lines, and I have squashed multiple commits if they address the same issue. In addition, my commits follow the guidelines from "[How to write a good git commit message](http://chris.beams.io/posts/git-commit/)":
  1. Subject is separated from body by a blank line
  1. Subject is limited to 50 characters (not including Jira issue reference)
  1. Subject does not end with a period
  1. Subject uses the imperative mood ("add", not "adding")
  1. Body wraps at 72 characters
  1. Body explains "what" and "why", not "how"

### Documentation

- [x] In case of new functionality, my PR adds documentation that describes how to use it.
  - When adding new operators/hooks/sensors, the autoclass documentation generation needs to be added.
  - All the public functions and the classes in the PR contain docstrings that explain what it does

### Code Quality

- [x] Passes `flake8`
