From 2f43280be0cbcf140f66bfc8b9ddb6dd33d8e257 Mon Sep 17 00:00:00 2001 From: duguangting Date: Mon, 9 Apr 2018 11:39:15 +0800 Subject: [PATCH] =?UTF-8?q?yaml=20config=20supported=C2=A0=F0=9F=A4=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++++++++++++- pyconf/__init__.py | 3 ++- pyconf/api.py | 5 ++-- pyconf/ini_config.py | 1 - pyconf/yml_config.py | 10 ++++++++ requirements.txt | 2 ++ setup.py | 2 +- tests/sample.yml | 20 ++++++++++++++++ tests/test_yaml_configs.py | 47 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 pyconf/yml_config.py create mode 100644 requirements.txt create mode 100644 tests/sample.yml create mode 100644 tests/test_yaml_configs.py diff --git a/README.md b/README.md index 6896d6e..7a1a256 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pyconf -Configuration for Humans +Configuration for Humans in ***Python3***. **pyconf** is an INI/py/yml configuration parsing package, written for humans. @@ -36,6 +36,16 @@ print(c['path']) # some_path ``` +yaml config file +--- + +```python +import pyconf +c = pyconf.load('tests/sample.yml', config_class=pyconf.YamlConfig) +print(c['path']) +# some_path +``` + Tests ===== @@ -44,11 +54,17 @@ Run the tests with ```bash python -m tests.test_ini_configs python -m tests.test_py_configs +python -m tests.test_yaml_configs ``` History === +0.1.1 +--- + +1. support the yaml config file + 0.1.0 --- diff --git a/pyconf/__init__.py b/pyconf/__init__.py index 4115b70..2e4cf1b 100644 --- a/pyconf/__init__.py +++ b/pyconf/__init__.py @@ -16,11 +16,12 @@ """ __title__ = 'pyconf' -__version__ = '0.1.0' +__version__ = '0.1.1' __author__ = 'Kevin Du' __license__ = 'MIT' from .ini_config import IniConfig from .py_config import PyConfig +from .yml_config import YamlConfig from .base_config import BaseConfig, Section from .api import load diff --git a/pyconf/api.py b/pyconf/api.py index fca21fe..1832f18 100644 --- a/pyconf/api.py +++ b/pyconf/api.py @@ -1,15 +1,16 @@ from .ini_config import IniConfig from .py_config import PyConfig +from .yml_config import YamlConfig -support_configs = (IniConfig, PyConfig) +support_configs = (IniConfig, PyConfig, YamlConfig) def load(config_file, config_class=IniConfig): """Constructs and returns a :class:`Config ` instance. :param config_file: configuration file to be parsed - :param ext: configuration extension + :param config_class: configuration extension Usage:: diff --git a/pyconf/ini_config.py b/pyconf/ini_config.py index d7dede5..55ff0bf 100644 --- a/pyconf/ini_config.py +++ b/pyconf/ini_config.py @@ -21,7 +21,6 @@ def load(self, config_file): """Parse an INI configuration file. :param config_file: configuration file to be loaded. - :param silent: set to ``True`` if you want silent failure for missing files. """ current_section = None diff --git a/pyconf/yml_config.py b/pyconf/yml_config.py new file mode 100644 index 0000000..84b2095 --- /dev/null +++ b/pyconf/yml_config.py @@ -0,0 +1,10 @@ +import yaml +from .py_config import PyConfig + + +class YamlConfig(PyConfig): + + def load(self, config_file): + with open(config_file) as config: + config_obj = yaml.load(config) + self._add_section_recursive(config_obj, self.sections['root']) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a3277ed --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +certifi==2018.1.18 +PyYAML==3.12 diff --git a/setup.py b/setup.py index b6cacd8..145c2b7 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ try: readme = open('README.md').read() except: - readme = 'pyconf: Configuration for Humans. Support ini config file, python config file' + readme = 'pyconf: Configuration for Humans. Support ini config file, python config file, yaml config file' setup( name=pyconf.__title__, diff --git a/tests/sample.yml b/tests/sample.yml new file mode 100644 index 0000000..49eb984 --- /dev/null +++ b/tests/sample.yml @@ -0,0 +1,20 @@ +path: some_path +hosts: ["example.com", "http://bing.com", "ssh.com:23", "www.qwe.asd"] + +section: { + "attr1": 7.1, + "attr2": 42, + "foo": 123 +} + +section2: { + "inner_section": { + "test_section": { + 1: 2 + }, + "two": 2, + }, + "attr1": 7.1, + "attr2": 42, + "foo": 123 +} diff --git a/tests/test_yaml_configs.py b/tests/test_yaml_configs.py new file mode 100644 index 0000000..09d223d --- /dev/null +++ b/tests/test_yaml_configs.py @@ -0,0 +1,47 @@ +import unittest + +from pyconf import api, YamlConfig, Section +from typing import Sequence + + +class TestPyConfig(unittest.TestCase): + + def setUp(self): + self.sample_config_filename = 'tests/sample.yml' + self.missing_config_filename = 'missing.yml' + + def test_load_general(self): + """Check if a valid config file is parsed correctly""" + + self.assertIsInstance(api.load(self.sample_config_filename, config_class=YamlConfig), YamlConfig) + + def test_load_missing_file(self): + """Check if the correct exception in raised when a missing config file is attempted to parse""" + + with self.assertRaises(FileNotFoundError): + api.load(self.missing_config_filename) + + def test_load_check_types(self): + """Check automatic float, integer, and boolean value conversion""" + config_data = api.load(self.sample_config_filename, config_class=YamlConfig) + self.assertIsInstance(config_data['path'], str) + self.assertIsInstance(config_data['hosts'], Sequence) + self.assertIsInstance(config_data['hosts'][0], str) + + self.assertIsInstance(config_data['section'], Section) + self.assertIsInstance(config_data['section']['attr1'], float) + self.assertIsInstance(config_data['section']['attr2'], int) + self.assertIsInstance(config_data['section']['foo'], int) + + self.assertIsInstance(config_data['section2'], Section) + self.assertIsInstance(config_data['section2']['attr1'], float) + self.assertIsInstance(config_data['section2']['attr2'], int) + self.assertIsInstance(config_data['section2']['foo'], int) + self.assertIsInstance(config_data['section2']['inner_section'], Section) + self.assertIsInstance(config_data['section2']['inner_section']['two'], int) + self.assertIsInstance(config_data['section2']['inner_section']['test_section'], Section) + self.assertIsInstance(config_data['section2']['inner_section']['test_section'][1], int) + + +if __name__ == '__main__': + unittest.main()