diff --git a/MANIFEST.in b/MANIFEST.in index a88cbc8..e27dfbf 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,3 +2,4 @@ include README.md include requirements.txt include CHANGELOG.md include LICENSE +include hues/.hues.yml diff --git a/hues/.hues.yml b/hues/.hues.yml new file mode 100644 index 0000000..c5032e7 --- /dev/null +++ b/hues/.hues.yml @@ -0,0 +1,5 @@ +colors: + default: white +themes: + powerline: + right_sep:  diff --git a/hues/console.py b/hues/console.py index 0b76c1a..43ab6eb 100644 --- a/hues/console.py +++ b/hues/console.py @@ -1,7 +1,54 @@ # Unicorns '''Helper module for all the goodness.''' +import os +import sys +import yaml + from .huestr import Hues -class Console(object): - def __init__(self): - pass +CONFIG_FNAME = '.hues.yml' + + +class InvalidConfiguration(Exception): + '''Raise when configuration is invalid.''' + + +class _Console(object): + def __init__(self, stdout=sys.stdout, stderr=sys.stderr): + self.stdout = stdout + self.stderr = stderr + self.config = self._load_config() + + @staticmethod + def _load_config(): + '''Find and load configuration params. + Config files are loaded in the following order: + - Beginning from current working dir, all the way to the root. + - User home (~). + - Module dir (defaults). + ''' + def _load(cdir, recurse=False): + confl = os.path.join(cdir, CONFIG_FNAME) + try: + with open(confl, 'r') as fp: + conf = yaml.safe_load(fp) + if type(conf) is not dict: + raise InvalidConfiguration('Configuration at %s is not a dictionary.' % confl) + return conf + except OSError: + parent = os.path.dirname(cdir) + if recurse and parent != cdir: + return _load(parent, recurse=True) + else: + return dict() + except yaml.YAMLError: + raise InvalidConfiguration('Configuration at %s is an invalid YAML file.' % confl) + + conf = _load(os.path.dirname(__file__)) + + home_conf = _load(os.path.expanduser('~')) + local_conf = _load(os.path.abspath(os.curdir), recurse=True) + + conf.update(home_conf) + conf.update(local_conf) + return conf diff --git a/requirements.txt b/requirements.txt index 097e65b..4e3a371 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,19 @@ codeclimate-test-reporter==0.1.2 +coverage==4.2 +extras==1.0.0 +fixtures==3.0.0 +linecache2==1.0.0 +mox3==0.18.0 +pbr==1.10.0 py==1.4.31 +pyfakefs==2.7 pytest==3.0.2 pytest-cov==2.3.1 pytest-runner==2.9 +python-mimeparse==1.5.2 +PyYAML==3.12 requests==2.11.1 +six==1.10.0 +testtools==2.2.0 +traceback2==1.4.0 +unittest2==1.1.0 diff --git a/setup.py b/setup.py index 438c981..63af253 100644 --- a/setup.py +++ b/setup.py @@ -15,9 +15,9 @@ vtp = re.search(rex, fp.read(), re.M).groups() __version__ = '.'.join(vtp) -install_requires = [] +install_requires = ['PyYAML',] setup_requires = ['pytest-runner',] -test_requirements = ['pytest', 'coverage',] +test_requirements = ['pytest', 'coverage', 'pyfakefs'] setup( diff --git a/tests/test_console.py b/tests/test_console.py new file mode 100644 index 0000000..f7a090d --- /dev/null +++ b/tests/test_console.py @@ -0,0 +1,61 @@ +import os +import pyfakefs.fake_filesystem_unittest as fake_fs_unittest + +CONFIG_FNAME = '.hues.yml' +usr_conf = os.path.join(os.path.expanduser('~'), CONFIG_FNAME) +mod_conf = os.path.join(os.path.dirname(__file__), '..', 'hues', CONFIG_FNAME) + +with open(mod_conf, 'r') as fp: + default_conf = fp.read() + +home_conf = ''' +colors: + default: red +''' +local_conf = ''' +colors: + default: green +''' +invalid_conf = '''Invalid Conf, but valid YAML.''' +invalid_yaml = '''Nested: Dicts: Are: Invalid!''' + +from hues.console import _Console, InvalidConfiguration + + +class Test_Console(fake_fs_unittest.TestCase): + def setUp(self): + self.setUpPyfakefs() + self.fs.CreateFile(mod_conf, contents=default_conf) + self.fs.CreateFile(usr_conf, contents=home_conf) + self.fs.CreateFile('/var/foo/.hues.yml', contents=local_conf) + self.fs.CreateFile('/var/invalid/.hues.yml', contents=invalid_conf) + self.fs.CreateFile('/var/invalidyml/.hues.yml', contents=invalid_yaml) + self.fs.CreateDirectory('/var/foo/bar/baz') + self.fs.CreateDirectory('/var/doom/baz') + + def test_home_config(self): + os.chdir('/var/doom/baz') + cs = _Console() + assert cs.config['colors']['default'] == 'red' + + def test_local_config(self): + os.chdir('/var/foo') + cs = _Console() + assert cs.config['colors']['default'] == 'green' + + def test_local_nested_config(self): + os.chdir('/var/foo/bar/baz') + cs = _Console() + assert cs.config['colors']['default'] == 'green' + + def test_invalid_config(self): + os.chdir('/var/invalid') + with self.assertRaises(InvalidConfiguration) as e: + cs = _Console() + assert 'not a dictionary' in str(e.exception) + + def test_invalid_yaml(self): + os.chdir('/var/invalidyml') + with self.assertRaises(InvalidConfiguration) as e: + cs = _Console() + assert 'invalid YAML' in str(e.exception)