diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f8eff7a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: python + +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "pypy" + +sudo: false + +install: + - pip install flake8 + +script: + - | + flake8 flask_env.py + python test_flask_env.py diff --git a/MANIFEST.in b/MANIFEST.in index 26af1a5..27b1a6c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include LICENSE README.rst flask_env.py +include LICENSE README.rst flask_env.py test_flask_env.py diff --git a/README.rst b/README.rst index 2ee8727..c61733c 100644 --- a/README.rst +++ b/README.rst @@ -81,11 +81,6 @@ ENV_PREFIX The prefix will be removed from the environment variable name when setting in the configuration. (default: :code:`''`, example: :code:`ENV_PREFIX = 'MYAPP_'`) -AS_JSON - Whether or not to try and parse each configuration value as JSON. - This will ensure that when setting variables as integers/null/booleans that they properly get parsed in their applicable Python types. - (default: :code:`True`, example: :code:`AS_JSON = False`) - Setting configuration values ---------------------------- @@ -99,4 +94,3 @@ You can set the :code:`flask-env` configuration settings directly on your Flask class Configuration(metaclass=MetaFlaskEnv): ENV_PREFIX = 'MYAPP_' - AS_JSON = False diff --git a/flask_env.py b/flask_env.py index 5f88f90..dd4d554 100644 --- a/flask_env.py +++ b/flask_env.py @@ -1,12 +1,4 @@ -import json import os -import sys - - -# DEV: In `python3` we raise `JSONDecodeError` instead of `ValueError` on a bad parse -json_decode_error = ValueError -if sys.version_info.major == 3: - json_decode_error = json.decoder.JSONDecodeError class MetaFlaskEnv(type): @@ -21,7 +13,6 @@ class MetaFlaskEnv(type): # Get our internal settings prefix = dict.get('ENV_PREFIX', '') - as_json = dict.get('AS_JSON', True) # Override default configuration from environment variables for key, value in os.environ.items(): @@ -32,12 +23,21 @@ class MetaFlaskEnv(type): # Strip the prefix from the environment variable name key = key[len(prefix):] - # Attempt to parse values as JSON if requested (default behavior) - # DEV: This will ensure that doing `PREFIX_PORT=8000` will result in `int(8000)` and not `str(8000)` - if as_json: + # If value is "true" or "false", parse as a boolean + # Otherwise, if it contains a "." then try to parse as a float + # Otherwise, try to parse as an integer + # If all else fails, just keep it a string + if value.lower() in ('true', 'false'): + value = True if value.lower() == 'true' else False + elif '.' in value: + try: + value = float(value) + except ValueError: + pass + else: try: - value = json.loads(value) - except json_decode_error: + value = int(value) + except ValueError: pass # Update our config with the value from `os.environ` diff --git a/test_flask_env.py b/test_flask_env.py new file mode 100644 index 0000000..562da3a --- /dev/null +++ b/test_flask_env.py @@ -0,0 +1,94 @@ +import os +import unittest + +from flask_env import MetaFlaskEnv + + +class TestFlaskEnv(unittest.TestCase): + def _get_test_configuration(self, env_prefix=''): + """Helper to define a new configuration class using our MetaFlaskEnv""" + return MetaFlaskEnv('TestConfiguration', (object, ), dict( + ENV_PREFIX=env_prefix, + DEFAULT_SETTING='default_value', + )) + + def test_default_settings(self): + """A test to ensure that if no environment variable is set, we get the default value that is set""" + TestConfiguration = self._get_test_configuration() + self.assertEqual(TestConfiguration.DEFAULT_SETTING, 'default_value') + + def test_override_from_env(self): + """A test to ensure that an environment variable will override the default setting""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['DEFAULT_SETTING'] = 'set_by_env' + + TestConfiguration = self._get_test_configuration() + self.assertEqual(TestConfiguration.DEFAULT_SETTING, 'set_by_env') + + def test_only_set_on_env(self): + """A test to ensure that a setting only defined by an environment variable is still available""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['NEW_SETTING'] = 'set_by_env' + + TestConfiguration = self._get_test_configuration() + self.assertEqual(TestConfiguration.NEW_SETTING, 'set_by_env') + + def test_env_prefix(self): + """A test to ensure that the ENV_PREFIX setting functions as needed""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['TEST_DEFAULT_SETTING'] = 'set_by_env' + + TestConfiguration = self._get_test_configuration(env_prefix='TEST_') + self.assertEqual(TestConfiguration.DEFAULT_SETTING, 'set_by_env') + + def test_env_prefix_non_matching(self): + """A test to ensure that the ENV_PREFIX setting does not allow non-matching settings in""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['DEFAULT_SETTING'] = 'set_by_env' + + TestConfiguration = self._get_test_configuration(env_prefix='MYAPP_') + self.assertEqual(TestConfiguration.DEFAULT_SETTING, 'default_value') + + def test_parsing_boolean(self): + """A test to ensure that we properly parse booleans""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['IS_TRUE'] = 'true' + os.environ['IS_NOT_TRUE'] = 'true-ish' + os.environ['IS_FALSE'] = 'FALSE' + os.environ['IS_WACKY_FALSE'] = 'FaLSe' + + TestConfiguration = self._get_test_configuration() + self.assertEqual(TestConfiguration.IS_TRUE, True) + self.assertEqual(TestConfiguration.IS_NOT_TRUE, 'true-ish') + self.assertEqual(TestConfiguration.IS_FALSE, False) + self.assertEqual(TestConfiguration.IS_WACKY_FALSE, False) + + def test_parsing_float(self): + """A test to ensure that we properly parse floats""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['IS_FLOAT'] = '12.5' + os.environ['TRAILING_DOT'] = '12.' + os.environ['LEADING_DOT'] = '.12' + os.environ['IS_NOT_FLOAT'] = 'This is 6.5' + + TestConfiguration = self._get_test_configuration() + self.assertEqual(TestConfiguration.IS_FLOAT, 12.5) + self.assertEqual(TestConfiguration.TRAILING_DOT, 12.0) + self.assertEqual(TestConfiguration.LEADING_DOT, 0.12) + self.assertEqual(TestConfiguration.IS_NOT_FLOAT, 'This is 6.5') + + def test_parsing_int(self): + """A test to ensure that we properly parse integers""" + # DEV: We have to set the environment variable first, since they get loaded into the class on definition + os.environ['IS_INT'] = '12' + os.environ['IS_ZERO'] = '0' + os.environ['IS_NOT_INT'] = '12fa' + + TestConfiguration = self._get_test_configuration() + self.assertEqual(TestConfiguration.IS_INT, 12) + self.assertEqual(TestConfiguration.IS_ZERO, 0) + self.assertEqual(TestConfiguration.IS_NOT_INT, '12fa') + + +if __name__ == '__main__': + unittest.main()