From 76ab8bef280e4c0b53db05c30ee4041b70699fce Mon Sep 17 00:00:00 2001 From: Erik Taubeneck Date: Thu, 16 Apr 2015 09:30:26 -0400 Subject: [PATCH] working refactor not requiring Flask import from ordbok, doc updates needed --- ordbok/__init__.py | 7 +++ ordbok/config_env.py | 6 +-- ordbok/config_file.py | 8 ++- ordbok/flask_helper.py | 59 ++++++--------------- ordbok/ordbok.py | 59 ++++++++++----------- ordbok/util.py | 13 +++++ tests/base_tests.py | 114 +++++++++++++++++------------------------ 7 files changed, 116 insertions(+), 150 deletions(-) create mode 100644 ordbok/__init__.py create mode 100644 ordbok/util.py diff --git a/ordbok/__init__.py b/ordbok/__init__.py new file mode 100644 index 0000000..8d9e3d8 --- /dev/null +++ b/ordbok/__init__.py @@ -0,0 +1,7 @@ +from .ordbok import Ordbok +from .config_file import ConfigFile +from .config_env import ConfigEnv +from .config_private import PrivateConfigFile + + +__all__ = ["Ordbok", "ConfigFile", "ConfigEnv", "PrivateConfigFile"] diff --git a/ordbok/config_env.py b/ordbok/config_env.py index 5dcee41..89dabd4 100644 --- a/ordbok/config_env.py +++ b/ordbok/config_env.py @@ -6,7 +6,7 @@ class ConfigEnv(ConfigFile): def __init__(self, config): self.config = config - self.keyword = '{}_env_config'.format(self.config.near_miss_key) + self.keyword = '{}_env_config'.format(self.config.namespace) self.required_keys = [] self.keyword_lookup = {} self.loaded = False @@ -20,9 +20,9 @@ def add_required_key(self, key, value): def _load(self, _): environ = { - key.replace(self.config.near_miss_key.upper()+'_', ''): value + key.replace(self.config.namespace.upper()+'_', ''): value for key, value in os.environ.items() if value and - key.startswith(self.config.near_miss_key.upper())} + key.startswith(self.config.namespace.upper())} for key, value in environ.items(): self.config[key] = yaml.load(value) diff --git a/ordbok/config_file.py b/ordbok/config_file.py index cf3da09..e00d593 100644 --- a/ordbok/config_file.py +++ b/ordbok/config_file.py @@ -4,16 +4,14 @@ class ConfigFile(object): - def __init__(self, filename, config=None, envs=None): + def __init__(self, filename, envs=None): self.filename = filename self.envs = envs - if config: - self.init_config(config) def init_config(self, config): self.config = config self.keyword = '{}_{}'.format( - self.config.near_miss_key, os.path.splitext(self.filename)[0]) + self.config.namespace, os.path.splitext(self.filename)[0]) self.required_keys = [] self.config_file_path = os.path.join( self.config.config_cwd, self.config.config_dir, self.filename) @@ -55,7 +53,7 @@ def _load(self, config_files_lookup): key, self.filename) ) if (isinstance(value, six.string_types) and - value.startswith(self.config.near_miss_key)): + value.startswith(self.config.namespace)): config_files = [k for k in config_files_lookup.keys() if value.startswith(k)] if len(config_files) == 0: diff --git a/ordbok/flask_helper.py b/ordbok/flask_helper.py index df19fba..a2d9130 100644 --- a/ordbok/flask_helper.py +++ b/ordbok/flask_helper.py @@ -1,46 +1,19 @@ from .ordbok import Ordbok -from flask import Flask as BaseFlask, Config as BaseConfig - -class OrdbokFlaskConfig(BaseConfig, Ordbok): - """ - Extented version of the builtin Flask `Config` that inherits - a `from_yaml` method from Ordbok so that config varibables - can be defined there. - """ - def __init__(self, *args, **kwargs): - self.set_defaults(**kwargs) - return super(OrdbokFlaskConfig, self).__init__(*args, **kwargs) - - @property - def config_cwd(self): - return self.root_path - - -def make_config(self, instance_relative=False): - root_path = self.root_path - if instance_relative: - root_path = self.instance_path - return self.config_class(root_path, self.default_config) - - -def run(self, *args, **kwargs): - if self.config_class is not OrdbokFlaskConfig: - raise Exception( - 'Cannot override Flask.run() without using OrdbokFlaskConfig ' - 'as Flask.config_class.') - if kwargs.get('use_reloader') is not False and self.debug: - kwargs.setdefault('extra_files', []) - kwargs['extra_files'].extend(self.config.config_file_names) - return super(Flask, self).run(*args, **kwargs) - - -class Flask(BaseFlask): - """ - Extened version of `Flask` that implements the custom config class - defined above. - """ - config_class = OrdbokFlaskConfig - make_config = make_config - run = run +class FlaskOrdbok(Ordbok): + def __init__(self, app=None, **kwargs): + if app: + self.init_app(app) + return super(FlaskOrdbok, self).__init__(**kwargs) + + def init_app(self, app): + self._root_path = app.config.root_path + + def app_run(self, app, *args, **kwargs): + if kwargs.get('use_reloader') is not False and app.debug: + extra_files = kwargs.get('extra_files', []) + extra_files.extend(self.config_file_names) + if extra_files: + kwargs['extra_files'] = extra_files + return app.run(*args, **kwargs) diff --git a/ordbok/ordbok.py b/ordbok/ordbok.py index 9348f19..e454fad 100644 --- a/ordbok/ordbok.py +++ b/ordbok/ordbok.py @@ -1,64 +1,59 @@ import os import six -from .config_file import ConfigFile +from .util import create_config_file from .config_env import ConfigEnv class Ordbok(dict): - def __init__(self, **kwargs): - self.set_defaults(**kwargs) + def __init__(self, config_files=None, config_dir='config', + include_env=True, namespace='ordbok', + default_environment='development', **kwargs): + self.config_files = config_files + if not self.config_files: + self.config_files = ['config.yml', 'local_config.yml'] + self.config_dir = config_dir + self.include_env = include_env + self.namespace = namespace + self.default_environment = default_environment + self.loaded = False return super(Ordbok, self).__init__(**kwargs) - def set_defaults(self, **kwargs): - self.config_files = [] - self.config_dir = kwargs.get('config_dir', 'config') - self.custom_config_files = kwargs.get( - 'custom_config_files', ['config.yml', 'local_config.yml']) - self.include_env = kwargs.get('include_env', True) - self.near_miss_key = kwargs.get('near_miss_key', 'ordbok') - self.default_environment = kwargs.get( - 'default_environment', 'development') - - def update_defaults(self, **kwargs): - self.config_dir = kwargs.get('config_dir', self.config_dir) - self.custom_config_files = kwargs.get( - 'custom_config_files', self.custom_config_files) - self.include_env = kwargs.get('include_env', self.include_env) - self.near_miss_key = kwargs.get('near_miss_key', self.near_miss_key) - self.default_environment = kwargs.get( - 'default_environment', self.default_environment) - @property def config_cwd(self): + if hasattr(self, '_root_path'): + return self._root_path return os.getcwd() @property def config_file_names(self): - return [getattr(config_file, 'config_file_path', None) - for config_file in self.config_files if - getattr(config_file, 'config_file_path', None)] + return ( + [getattr(config_file, 'config_file_path', None) + for config_file in self.config_files if + hasattr(config_file, 'config_file_path')] + + [config_file for config_file in self.config_files if + isinstance(config_file, six.string_types)] + ) def load(self): + if self.loaded: + raise Exception('Ordbok instance can only be loaded once.') if not self.get('ENVIRONMENT'): self['ENVIRONMENT'] = os.environ.get( - '{}_ENVIRONMENT'.format(self.near_miss_key.upper()), + '{}_ENVIRONMENT'.format(self.namespace.upper()), self.default_environment).lower() - self.config_files = [f for f in self.custom_config_files - if isinstance(f, ConfigFile)] + self.config_files = [create_config_file(f) for f in self.config_files] + for f in self.config_files: f.init_config(self) - self.config_files.extend( - [ConfigFile(f, self) for f in self.custom_config_files - if isinstance(f, six.string_types)]) - if self.include_env: self.config_files.append(ConfigEnv(self)) config_files_lookup = {cf.keyword: cf for cf in self.config_files} for config_file in self.config_files: config_file.load(config_files_lookup) + self.loaded = True @property def private_file_key(self): diff --git a/ordbok/util.py b/ordbok/util.py new file mode 100644 index 0000000..9c01a7b --- /dev/null +++ b/ordbok/util.py @@ -0,0 +1,13 @@ +import six +from .config_file import ConfigFile + + +def create_config_file(f): + if isinstance(f, ConfigFile): + return f + elif isinstance(f, six.string_types): + return ConfigFile(f) + else: + raise TypeError( + 'Ordbok.config_files can only be derived from ' + 'ordbok.ConfigFile or the filename of a config file') diff --git a/tests/base_tests.py b/tests/base_tests.py index 4b8cf80..e402255 100644 --- a/tests/base_tests.py +++ b/tests/base_tests.py @@ -5,10 +5,10 @@ from copy import deepcopy from yaml.scanner import ScannerError -from flask import Flask as BaseFlask +from flask import Flask + +from ordbok.flask_helper import FlaskOrdbok from ordbok import Ordbok, PrivateConfigFile -from ordbok.flask_helper import ( - Flask as OrdbokFlask, OrdbokFlaskConfig, make_config) from tests.files import ( fudged_config_files, fudged_config_no_local_file, @@ -19,8 +19,16 @@ class OrdbokTestCase(unittest.TestCase): def setUp(self): self.ordbok = Ordbok() + def test_ordbok_defaults(self): + self.assertEqual(self.ordbok.config_files, + ['config.yml', 'local_config.yml']) + self.assertEqual(self.ordbok.config_dir, 'config') + self.assertTrue(self.ordbok.include_env) + self.assertEqual(self.ordbok.namespace, 'ordbok') + self.assertEqual(self.ordbok.default_environment, 'development') + @fudge.patch('six.moves.builtins.open') - def test_ordbok_default(self, fudged_open): + def test_ordbok_load(self, fudged_open): fudged_open.is_callable().calls(fake_file_factory(fudged_config_files)) self.ordbok.load() self.assertEquals(self.ordbok['ENVIRONMENT'], 'development') @@ -105,8 +113,8 @@ def setUp(self): self.private_config_file = PrivateConfigFile( 'private_config.yml', envs=['production']) self.ordbok = Ordbok( - custom_config_files=['config.yml', 'local_config.yml', - self.private_config_file] + config_files=['config.yml', 'local_config.yml', + self.private_config_file] ) @unittest.skipIf(os.environ.get('SKIP_ENCRYPT_TEST'), @@ -161,79 +169,63 @@ def test_ordbok_private_config_no_envs( self.assertTrue(mock_load_yaml.called) -class OrdbokDefaultsTestCase(OrdbokTestCase): +class OrdbokDefaultsTestCase(unittest.TestCase): def test_update_all_defaults(self): - self.ordbok.update_defaults( - config_dir='ordbok_config', - custom_config_files=['config.yml'], - include_env=False, - near_miss_key='ordbok_foo', - default_environment='testing', - ) + Ordbok(config_dir='ordbok_config', + config_files=['config.yml'], + include_env=False, + namespace='ordbok_foo', + default_environment='testing',) def test_update_config_dir(self): - self.ordbok.update_defaults( - config_dir='ordbok_config', - ) + Ordbok(config_dir='ordbok_config',) - def test_update_custom_config_files(self): - self.ordbok.update_defaults( - custom_config_files=['config.yml'], - ) + def test_update_config_files(self): + Ordbok(config_files=['config.yml'],) def test_update_include_env(self): - self.ordbok.update_defaults( - include_env=False, - ) + Ordbok(include_env=False,) - def test_update_near_miss_key(self): - self.ordbok.update_defaults( - near_miss_key='ordbok_foo', - ) + def test_update_namespace(self): + Ordbok(namespace='ordbok_foo',) def test_update_default_environment(self): - self.ordbok.update_defaults( - default_environment='testing', - ) + Ordbok(default_environment='testing',) class FlaskOrdbokTestCase(OrdbokTestCase): def setUp(self): - self.app = OrdbokFlask(__name__) - self.ordbok = self.app.config - self.ordbok.root_path = os.getcwd() # the fudged files are here - - def test_flask_helper(self): - self.assertIsInstance(self.app.config, OrdbokFlaskConfig) + self.app = Flask(os.getcwd()) # fudged files think they are in cwd + self.ordbok = FlaskOrdbok(app=self.app) - def test_flask_reloader(self): - BaseFlask.run = mock.MagicMock(return_value=True) + @mock.patch.object(Flask, 'run') + def test_flask_reloader(self, fudged_flask_run): self.ordbok.load() self.app.debug = True - self.app.run() - BaseFlask.run.assert_called() - BaseFlask.run.assert_called_with( + self.ordbok.app_run(self.app) + fudged_flask_run.assert_called() + fudged_flask_run.assert_called_with( extra_files=self.ordbok.config_file_names) - def test_flask_reloader_debug_off(self): - BaseFlask.run = mock.MagicMock(return_value=True) + @mock.patch.object(Flask, 'run') + def test_flask_reloader_debug_off(self, fudged_flask_run): self.ordbok.load() - self.app.run() - BaseFlask.run.assert_called() - BaseFlask.run.assert_called_with() # no extra files + self.ordbok.app_run(self.app) + fudged_flask_run.assert_called() + fudged_flask_run.assert_called_with() # no extra files - def test_flask_reloader_use_reloader_false(self): - BaseFlask.run = mock.MagicMock(return_value=True) + @mock.patch.object(Flask, 'run') + def test_flask_reloader_use_reloader_false(self, fudged_flask_run): self.ordbok.load() - self.app.run(use_reloader=False) - BaseFlask.run.assert_called() - BaseFlask.run.assert_called_with(use_reloader=False) # no extra files + self.ordbok.app_run(self.app, use_reloader=False) + fudged_flask_run.assert_called() + fudged_flask_run.assert_called_with(use_reloader=False) # no extra files -class SpecialFlaskOrbokTestCase(unittest.TestCase): +class SpecialFlaskOrdbokTestCase(unittest.TestCase): def setUp(self): - self.app = OrdbokFlask(__name__) - self.ordbok = self.app.config + self.app = Flask(__name__) + self.ordbok = FlaskOrdbok(app=self.app) def test_root_path(self): """ @@ -242,15 +234,3 @@ def test_root_path(self): this for the the tests. """ self.assertEquals(self.app.root_path, self.ordbok.config_cwd) - - -class BaseFlaskOrdbokTestCase(unittest.TestCase): - def setUp(self): - BaseFlask.config_class = OrdbokFlaskConfig - BaseFlask.make_config = make_config - self.app = BaseFlask(__name__) - self.ordbok = self.app.config - self.ordbok.root_path = os.getcwd() # the fudged files are here - - def test_flask_helper(self): - self.assertIsInstance(self.app.config, OrdbokFlaskConfig)