././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/0000755000175000017500000000000014472630705010333 5ustar00tt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/AUTHORS0000644000175000017500000000044214472354700011401 0ustar00ttKaptan is written and maintained by various authors and contributors: - Emre Yılmaz - Tony Narlock - Berker Peksag Patches and suggestions: - @cenkalti - @marksteve - @Wessie - @pradyunsg For an up to date list of contributors, see: https://github.com/emre/kaptan/graphs/contributors ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/LICENSE0000644000175000017500000000300214472354700011331 0ustar00ttBSD 3-Clause License Copyright (c) 2013-, Emre Yılmaz and contributors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/MANIFEST.in0000644000175000017500000000031214472354700012063 0ustar00ttinclude LICENSE AUTHORS README.md include tests.py recursive-include requirements * recursive-include kaptan * recursive-exclude kaptan *.pyc recursive-exclude kaptan *.pyo recursive-include tests *.py ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/PKG-INFO0000644000175000017500000001453514472630705011440 0ustar00ttMetadata-Version: 2.1 Name: kaptan Version: 0.6.0 Summary: Configuration manager Home-page: https://github.com/emre/kaptan Author: Emre Yilmaz Author-email: mail@emreyilmaz.me License: BSD Classifier: Development Status :: 5 - Production/Stable Classifier: Operating System :: POSIX Classifier: Operating System :: MacOS :: MacOS X Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 License-File: LICENSE License-File: AUTHORS kaptan ====== |pypi| |docs| |build-status| |coverage| |license| configuration parser. installation ------------ .. code-block:: console $ pip install kaptan Also available as a package on FreeBSD, Debian, Arch Linux and Slackware. usage ----- **supported handlers** - dict - json - yaml - .ini - python file **default (dict) handler** .. code-block:: python config = kaptan.Kaptan() config.import_config({ 'environment': 'DEV', 'redis_uri': 'redis://localhost:6379/0', 'debug': False, 'pagination': { 'per_page': 10, 'limit': 20, } }) print config.get("pagination.limit") # output: 20 **json handler** .. code-block:: python config = kaptan.Kaptan(handler="json") config.import_config('{"everything": 42}') print config.get("everything") # output: 42 **yaml handler** .. code-block:: python config = kaptan.Kaptan(handler="yaml") config.import_config(""" product: price: value: 12.65 currency_list: 1. TL 2. EURO """) print config.get("product.price.currency_list.0") # output: TL or you can get from directly from the filename: ``config.import_config("configuration.yaml")`` **.ini handler** config.ini .. code-block:: ini [development] database_uri = mysql://root:123456@localhost/posts [production] database_uri = mysql://poor_user:poor_password@localhost/poor_posts .. code-block:: python config = kaptan.Kaptan(handler="ini") config.import_config('config.ini') print config.get("production.database_uri") # output: mysql://poor_user:poor_password@localhost/poor_posts **file handler** config.py .. code-block:: python DATABASE = 'mysql://root:123456@localhost/posts' DEBUG = False PAGINATION = { 'per_page': 10, 'limit': 20, } .. code-block:: python config = kaptan.Kaptan(handler="file") config.import_config('config') print config.get("DEBUG") # output: False exporting configuration ----------------------- .. code-block:: python config = kaptan.Kaptan(handler="file") config.import_config({ 'environment': 'DEV', 'redis_uri': 'redis://localhost:6379/0', 'debug': False, 'pagination': { 'per_page': 10, 'limit': 20, } }) print config.export("yaml") **output**: .. code-block:: yaml debug: false environment: DEV pagination: {limit: 20, per_page: 10} redis_uri: redis://localhost:6379/0 ``print config.export("json")`` outputs unindented json. ``.export`` accepts kwargs which pass into `json.dumps`. .. _json.dumps: http://docs.python.org/2/library/json.html#json.dump .. code-block:: python print config.export("json", indent=4) **output**: .. code-block:: json { "environment": "DEV", "debug": false, "pagination": { "per_page": 10, "limit": 20 }, "redis_uri": "redis://localhost:6379/0" } ``config.export('yaml')`` also supports the `kwargs for pyyaml`_. .. _kwargs for pyyaml: http://pyyaml.org/wiki/PyYAMLDocumentation#Dumper New in Version 0.5.7: ``config.export('yaml', safe=True)`` will use ``.safe_dump``. cli --- exporting (defaults to json) .. code-block:: console $ echo "environment: DEV" > config.yaml $ kaptan config.yaml --export json > config.json $ cat config.json {"environment": "DEV"} getting a value .. code-block:: console $ kaptan config.yaml --key environment DEV specifying the handler .. code-block:: console $ mv config.yaml config.settings $ kaptan config.settings:yaml --export json {"environment": "DEV"} config from stdin .. code-block:: console $ echo '{"source": "stdin"}' | kaptan - {"source": "stdin"} $ echo 'source: stdin' | kaptan -:yaml {"source": "stdin"} merging configs .. code-block:: console $ echo "environment: PROD" > config.settings $ echo '{"source": "stdin"}' | kaptan - config.json config.settings:yaml {"environment": "PROD", "source": "stdin"} setting default handler .. code-block:: console $ echo "source: stdin" | kaptan --handler yaml - config.settings {"environment": "PROD", "source": "stdin"} writing json with yaml .. code-block:: console $ kaptan -:yaml -e json running tests ------------- with ``py.test``: .. code-block:: console $ py.test contributors ------------ - `Cenk Altı `_ - `Wesley Bitter `_ - `Mark Steve `_ - `Tony Narlock `_ - `Berker Peksag `_ - `Pradyun S. Gedam `_ see more at https://github.com/emre/kaptan/graphs/contributors. .. |pypi| image:: https://img.shields.io/pypi/v/kaptan.svg :alt: Python Package :target: http://badge.fury.io/py/kaptan .. |build-status| image:: https://github.com/emre/kaptan/actions/workflows/tests.yml/badge.svg :alt: Build Status :target: https://github.com/emre/kaptan/actions/workflows/tests.yml .. |coverage| image:: https://codecov.io/gh/emre/kaptan/branch/master/graph/badge.svg :alt: Code Coverage :target: https://codecov.io/gh/emre/kaptan .. |license| image:: https://img.shields.io/github/license/emre/kaptan.svg :alt: License .. |docs| image:: https://readthedocs.org/projects/kaptan/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://readthedocs.org/projects/kaptan/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693134257.0 kaptan-0.6.0/README.rst0000644000175000017500000001305114472626661012030 0ustar00ttkaptan ====== |pypi| |docs| |build-status| |coverage| |license| configuration parser. installation ------------ .. code-block:: console $ pip install kaptan Also available as a package on FreeBSD, Debian, Arch Linux and Slackware. usage ----- **supported handlers** - dict - json - yaml - .ini - python file **default (dict) handler** .. code-block:: python config = kaptan.Kaptan() config.import_config({ 'environment': 'DEV', 'redis_uri': 'redis://localhost:6379/0', 'debug': False, 'pagination': { 'per_page': 10, 'limit': 20, } }) print config.get("pagination.limit") # output: 20 **json handler** .. code-block:: python config = kaptan.Kaptan(handler="json") config.import_config('{"everything": 42}') print config.get("everything") # output: 42 **yaml handler** .. code-block:: python config = kaptan.Kaptan(handler="yaml") config.import_config(""" product: price: value: 12.65 currency_list: 1. TL 2. EURO """) print config.get("product.price.currency_list.0") # output: TL or you can get from directly from the filename: ``config.import_config("configuration.yaml")`` **.ini handler** config.ini .. code-block:: ini [development] database_uri = mysql://root:123456@localhost/posts [production] database_uri = mysql://poor_user:poor_password@localhost/poor_posts .. code-block:: python config = kaptan.Kaptan(handler="ini") config.import_config('config.ini') print config.get("production.database_uri") # output: mysql://poor_user:poor_password@localhost/poor_posts **file handler** config.py .. code-block:: python DATABASE = 'mysql://root:123456@localhost/posts' DEBUG = False PAGINATION = { 'per_page': 10, 'limit': 20, } .. code-block:: python config = kaptan.Kaptan(handler="file") config.import_config('config') print config.get("DEBUG") # output: False exporting configuration ----------------------- .. code-block:: python config = kaptan.Kaptan(handler="file") config.import_config({ 'environment': 'DEV', 'redis_uri': 'redis://localhost:6379/0', 'debug': False, 'pagination': { 'per_page': 10, 'limit': 20, } }) print config.export("yaml") **output**: .. code-block:: yaml debug: false environment: DEV pagination: {limit: 20, per_page: 10} redis_uri: redis://localhost:6379/0 ``print config.export("json")`` outputs unindented json. ``.export`` accepts kwargs which pass into `json.dumps`. .. _json.dumps: http://docs.python.org/2/library/json.html#json.dump .. code-block:: python print config.export("json", indent=4) **output**: .. code-block:: json { "environment": "DEV", "debug": false, "pagination": { "per_page": 10, "limit": 20 }, "redis_uri": "redis://localhost:6379/0" } ``config.export('yaml')`` also supports the `kwargs for pyyaml`_. .. _kwargs for pyyaml: http://pyyaml.org/wiki/PyYAMLDocumentation#Dumper New in Version 0.5.7: ``config.export('yaml', safe=True)`` will use ``.safe_dump``. cli --- exporting (defaults to json) .. code-block:: console $ echo "environment: DEV" > config.yaml $ kaptan config.yaml --export json > config.json $ cat config.json {"environment": "DEV"} getting a value .. code-block:: console $ kaptan config.yaml --key environment DEV specifying the handler .. code-block:: console $ mv config.yaml config.settings $ kaptan config.settings:yaml --export json {"environment": "DEV"} config from stdin .. code-block:: console $ echo '{"source": "stdin"}' | kaptan - {"source": "stdin"} $ echo 'source: stdin' | kaptan -:yaml {"source": "stdin"} merging configs .. code-block:: console $ echo "environment: PROD" > config.settings $ echo '{"source": "stdin"}' | kaptan - config.json config.settings:yaml {"environment": "PROD", "source": "stdin"} setting default handler .. code-block:: console $ echo "source: stdin" | kaptan --handler yaml - config.settings {"environment": "PROD", "source": "stdin"} writing json with yaml .. code-block:: console $ kaptan -:yaml -e json running tests ------------- with ``py.test``: .. code-block:: console $ py.test contributors ------------ - `Cenk Altı `_ - `Wesley Bitter `_ - `Mark Steve `_ - `Tony Narlock `_ - `Berker Peksag `_ - `Pradyun S. Gedam `_ see more at https://github.com/emre/kaptan/graphs/contributors. .. |pypi| image:: https://img.shields.io/pypi/v/kaptan.svg :alt: Python Package :target: http://badge.fury.io/py/kaptan .. |build-status| image:: https://github.com/emre/kaptan/actions/workflows/tests.yml/badge.svg :alt: Build Status :target: https://github.com/emre/kaptan/actions/workflows/tests.yml .. |coverage| image:: https://codecov.io/gh/emre/kaptan/branch/master/graph/badge.svg :alt: Code Coverage :target: https://codecov.io/gh/emre/kaptan .. |license| image:: https://img.shields.io/github/license/emre/kaptan.svg :alt: License .. |docs| image:: https://readthedocs.org/projects/kaptan/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://readthedocs.org/projects/kaptan/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/kaptan/0000755000175000017500000000000014472630705011611 5ustar00tt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135118.0 kaptan-0.6.0/kaptan/__about__.py0000644000175000017500000000043614472630416014073 0ustar00tt__title__ = 'kaptan' __package_name__ = 'kaptan' __version__ = '0.6.0' __description__ = 'Configuration manager' __email__ = 'mail@emreyilmaz.me' __url__ = 'https://github.com/emre/kaptan' __author__ = 'Emre Yilmaz' __license__ = 'BSD' __copyright__ = 'Copyright 2013-2019 Emre Yilmaz' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/__init__.py0000644000175000017500000001416614472354700013730 0ustar00tt# -*- coding: utf8 -*- """ kaptan ~~~~~~ configuration parser. :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals import argparse import os from ._compat import collections_abc from .handlers.dict_handler import DictHandler from .handlers.pyfile_handler import PyFileHandler from .handlers.ini_handler import IniHandler from .handlers.json_handler import JsonHandler from .handlers.yaml_handler import YamlHandler SENTINEL = object() HANDLER_EXT = { 'ini': 'ini', 'conf': 'ini', 'yaml': 'yaml', 'yml': 'yaml', 'json': 'json', 'py': 'file', } class Kaptan(object): HANDLER_MAP = { 'json': JsonHandler, 'dict': DictHandler, 'yaml': YamlHandler, 'file': PyFileHandler, 'ini': IniHandler, } def __init__(self, handler=None): self.configuration_data = dict() self.handler = None if handler: self.handler = self.HANDLER_MAP[handler]() def upsert(self, key, value): self.configuration_data.update({key: value}) return self def _is_python_file(self, value): """ Return True if the `value` is the path to an existing file with a `.py` extension. False otherwise """ ext = os.path.splitext(value)[1][1:] if ext == 'py' or os.path.isfile(value + '.py'): return True return False def import_config(self, value): if isinstance(value, dict): # load python dict self.handler = self.HANDLER_MAP['dict']() data = value elif os.path.isfile(value) and not self._is_python_file(value): if not self.handler: try: key = HANDLER_EXT.get(os.path.splitext(value)[1][1:], None) self.handler = self.HANDLER_MAP[key]() except: raise RuntimeError("Unable to determine handler") with open(value) as f: data = f.read() elif self._is_python_file(value): # is a python file self.handler = self.HANDLER_MAP[HANDLER_EXT['py']]() if not value.endswith('.py'): value += '.py' # in case someone is referring to a module data = os.path.abspath(os.path.expanduser(value)) if not os.path.isfile(data): raise IOError('File {0} not found.'.format(data)) else: if not self.handler: raise RuntimeError("Unable to determine handler") data = value self.configuration_data = self.handler.load(data) return self def _get(self, key): current_data = self.configuration_data for chunk in key.split('.'): if isinstance(current_data, collections_abc.Mapping): current_data = current_data[chunk] elif isinstance(current_data, collections_abc.Sequence): chunk = int(chunk) current_data = current_data[chunk] else: # A scalar type has been found return current_data return current_data def get(self, key=None, default=SENTINEL): if not key: # .get() or .get(''), return full config return self.export('dict') try: try: return self._get(key) except KeyError: raise KeyError(key) except ValueError: raise ValueError("Sequence index not an integer") except IndexError: raise IndexError("Sequence index out of range") except (KeyError, ValueError, IndexError): if default is not SENTINEL: return default raise def export(self, handler=None, **kwargs): if not handler: handler_class = self.handler else: handler_class = self.HANDLER_MAP[handler]() return handler_class.dump(self.configuration_data, **kwargs) def __handle_default_value(self, key, default): if default == SENTINEL: raise KeyError(key) return default def get_parser(): """Create and return argument parser. :rtype: :class:`argparse.ArgumentParser` :return: CLI Parser """ parser = argparse.ArgumentParser( prog=__package__, description='Configuration manager in your pocket' ) parser.add_argument('config_file', action='store', nargs='*', help="file/s to load config from") parser.add_argument('--handler', action='store', default='json', help="set default handler") parser.add_argument('-e', '--export', action='store', default='json', help="set format to export to") parser.add_argument('-k', '--key', action='store', help="set config key to get value of") return parser def main(): from sys import stdin parser = get_parser() args, ukargs = parser.parse_known_args() config = Kaptan() config_files = args.config_file + ukargs if not config_files: parser.print_help() parser.exit(1) def get_handlers(): for f in config_files: s = f.split(':') if len(s) != 2: s += [None] yield tuple(s) config_handlers = collections_abc.OrderedDict(list(get_handlers())) for config_file, handler in config_handlers.items(): is_stdin = config_file == '-' if is_stdin: handler = handler or args.handler else: ext = handler or os.path.splitext(config_file)[1][1:] handler = HANDLER_EXT.get(ext, args.handler) _config = Kaptan(handler=handler) if is_stdin: _config.import_config(stdin.read()) else: with open(config_file) as f: _config.import_config(f.read()) config.configuration_data.update(_config.configuration_data) if args.key: print(config.get(args.key)) else: print(config.export(args.export)) parser.exit(0) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/__main__.py0000644000175000017500000000007714472354700013705 0ustar00ttfrom kaptan import main if __name__ == '__main__': main() ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/_compat.py0000644000175000017500000000027614472354700013610 0ustar00tt# -*- coding: utf8 -*- # flake8: NOQA: F40 import sys PY2 = sys.version_info[0] == 2 if PY2: import collections as collections_abc else: import collections.abc as collections_abc ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/kaptan/handlers/0000755000175000017500000000000014472630705013411 5ustar00tt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/handlers/__init__.py0000644000175000017500000000067614472354700015531 0ustar00tt# -*- coding: utf8 -*- """ kaptan.handlers ~~~~~~~~~~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals class BaseHandler(object): """Base class for data handlers.""" def load(self, data): raise NotImplementedError def dump(self, data): raise NotImplementedError ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/handlers/dict_handler.py0000644000175000017500000000066414472354700016407 0ustar00tt# -*- coding: utf8 -*- """ kaptan.handlers.dict_handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals from . import BaseHandler class DictHandler(BaseHandler): def load(self, data): return data def dump(self, data): return data ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/handlers/ini_handler.py0000644000175000017500000000255514472354700016244 0ustar00tt# -*- coding: utf8 -*- """ kaptan.handlers.ini_handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals try: import ConfigParser as configparser from StringIO import StringIO configparser.RawConfigParser.read_file = configparser.RawConfigParser.readfp # NOQA except ImportError: # Python 3 import configparser from io import StringIO from . import BaseHandler class KaptanIniParser(configparser.RawConfigParser): def from_dict(self, dictionary): self._sections = dictionary def as_dict(self): d = dict(self._sections) for k in d: d[k] = dict(self._defaults, **d[k]) d[k].pop('__name__', None) return d class IniHandler(BaseHandler): def load(self, value): config = KaptanIniParser() # ConfigParser.ConfigParser wants to read value as file / IO config.read_file(StringIO(value)) return config.as_dict() def dump(self, data, file_=None): if file_ is None: raise NotImplementedError("Exporting .ini as string is not supported.") config = KaptanIniParser() config.from_dict(data) with open(file_, 'w') as fp: config.write(fp) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/handlers/json_handler.py0000644000175000017500000000075514472354700016436 0ustar00tt# -*- coding: utf8 -*- """ kaptan.handlers.json_handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals import json from . import BaseHandler class JsonHandler(BaseHandler): def load(self, data): return json.loads(data) def dump(self, data, **kwargs): return json.dumps(data, **kwargs) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/handlers/pyfile_handler.py0000644000175000017500000000305514472354700016751 0ustar00tt# -*- coding: utf8 -*- """ kaptan.handlers.pyfile_handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals import os import sys from . import BaseHandler def import_pyfile(pathname, mod_name=''): """Import the contents of filepath as a Python module. Parameters ---------- pathname: str Path to the .py file to be imported as a module mod_name: str Name of the module when imported Returns ------- mod The imported module Raises ------ IOError If file is not found """ if not os.path.isfile(pathname): raise IOError('File {0} not found.'.format(pathname)) if sys.version_info[0] == 3 and sys.version_info[1] > 2: # Python >= 3.3 import importlib.machinery loader = importlib.machinery.SourceFileLoader('', pathname) mod = loader.load_module(mod_name) else: # 2.6 >= Python <= 3.2 import imp mod = imp.load_source(mod_name, pathname) return mod class PyFileHandler(BaseHandler): def load(self, file_): module = import_pyfile(file_) data = dict() for key in dir(module): value = getattr(module, key) if not key.startswith("__"): data.update({key: value}) return data def dump(self, file_): raise NotImplementedError("Exporting python files is not supported.") ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/kaptan/handlers/yaml_handler.py0000644000175000017500000000126314472354700016422 0ustar00tt# -*- coding: utf8 -*- """ kaptan.handlers.yaml_handler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals import yaml from . import BaseHandler class YamlHandler(BaseHandler): def load(self, data, safe=True): if safe: func = yaml.safe_load else: func = yaml.load return func(data) def dump(self, data, safe=True, **kwargs): if safe: func = yaml.safe_dump else: func = yaml.dump return func(data, **kwargs) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/kaptan.egg-info/0000755000175000017500000000000014472630705013303 5ustar00tt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135300.0 kaptan-0.6.0/kaptan.egg-info/PKG-INFO0000644000175000017500000001453514472630704014407 0ustar00ttMetadata-Version: 2.1 Name: kaptan Version: 0.6.0 Summary: Configuration manager Home-page: https://github.com/emre/kaptan Author: Emre Yilmaz Author-email: mail@emreyilmaz.me License: BSD Classifier: Development Status :: 5 - Production/Stable Classifier: Operating System :: POSIX Classifier: Operating System :: MacOS :: MacOS X Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 License-File: LICENSE License-File: AUTHORS kaptan ====== |pypi| |docs| |build-status| |coverage| |license| configuration parser. installation ------------ .. code-block:: console $ pip install kaptan Also available as a package on FreeBSD, Debian, Arch Linux and Slackware. usage ----- **supported handlers** - dict - json - yaml - .ini - python file **default (dict) handler** .. code-block:: python config = kaptan.Kaptan() config.import_config({ 'environment': 'DEV', 'redis_uri': 'redis://localhost:6379/0', 'debug': False, 'pagination': { 'per_page': 10, 'limit': 20, } }) print config.get("pagination.limit") # output: 20 **json handler** .. code-block:: python config = kaptan.Kaptan(handler="json") config.import_config('{"everything": 42}') print config.get("everything") # output: 42 **yaml handler** .. code-block:: python config = kaptan.Kaptan(handler="yaml") config.import_config(""" product: price: value: 12.65 currency_list: 1. TL 2. EURO """) print config.get("product.price.currency_list.0") # output: TL or you can get from directly from the filename: ``config.import_config("configuration.yaml")`` **.ini handler** config.ini .. code-block:: ini [development] database_uri = mysql://root:123456@localhost/posts [production] database_uri = mysql://poor_user:poor_password@localhost/poor_posts .. code-block:: python config = kaptan.Kaptan(handler="ini") config.import_config('config.ini') print config.get("production.database_uri") # output: mysql://poor_user:poor_password@localhost/poor_posts **file handler** config.py .. code-block:: python DATABASE = 'mysql://root:123456@localhost/posts' DEBUG = False PAGINATION = { 'per_page': 10, 'limit': 20, } .. code-block:: python config = kaptan.Kaptan(handler="file") config.import_config('config') print config.get("DEBUG") # output: False exporting configuration ----------------------- .. code-block:: python config = kaptan.Kaptan(handler="file") config.import_config({ 'environment': 'DEV', 'redis_uri': 'redis://localhost:6379/0', 'debug': False, 'pagination': { 'per_page': 10, 'limit': 20, } }) print config.export("yaml") **output**: .. code-block:: yaml debug: false environment: DEV pagination: {limit: 20, per_page: 10} redis_uri: redis://localhost:6379/0 ``print config.export("json")`` outputs unindented json. ``.export`` accepts kwargs which pass into `json.dumps`. .. _json.dumps: http://docs.python.org/2/library/json.html#json.dump .. code-block:: python print config.export("json", indent=4) **output**: .. code-block:: json { "environment": "DEV", "debug": false, "pagination": { "per_page": 10, "limit": 20 }, "redis_uri": "redis://localhost:6379/0" } ``config.export('yaml')`` also supports the `kwargs for pyyaml`_. .. _kwargs for pyyaml: http://pyyaml.org/wiki/PyYAMLDocumentation#Dumper New in Version 0.5.7: ``config.export('yaml', safe=True)`` will use ``.safe_dump``. cli --- exporting (defaults to json) .. code-block:: console $ echo "environment: DEV" > config.yaml $ kaptan config.yaml --export json > config.json $ cat config.json {"environment": "DEV"} getting a value .. code-block:: console $ kaptan config.yaml --key environment DEV specifying the handler .. code-block:: console $ mv config.yaml config.settings $ kaptan config.settings:yaml --export json {"environment": "DEV"} config from stdin .. code-block:: console $ echo '{"source": "stdin"}' | kaptan - {"source": "stdin"} $ echo 'source: stdin' | kaptan -:yaml {"source": "stdin"} merging configs .. code-block:: console $ echo "environment: PROD" > config.settings $ echo '{"source": "stdin"}' | kaptan - config.json config.settings:yaml {"environment": "PROD", "source": "stdin"} setting default handler .. code-block:: console $ echo "source: stdin" | kaptan --handler yaml - config.settings {"environment": "PROD", "source": "stdin"} writing json with yaml .. code-block:: console $ kaptan -:yaml -e json running tests ------------- with ``py.test``: .. code-block:: console $ py.test contributors ------------ - `Cenk Altı `_ - `Wesley Bitter `_ - `Mark Steve `_ - `Tony Narlock `_ - `Berker Peksag `_ - `Pradyun S. Gedam `_ see more at https://github.com/emre/kaptan/graphs/contributors. .. |pypi| image:: https://img.shields.io/pypi/v/kaptan.svg :alt: Python Package :target: http://badge.fury.io/py/kaptan .. |build-status| image:: https://github.com/emre/kaptan/actions/workflows/tests.yml/badge.svg :alt: Build Status :target: https://github.com/emre/kaptan/actions/workflows/tests.yml .. |coverage| image:: https://codecov.io/gh/emre/kaptan/branch/master/graph/badge.svg :alt: Code Coverage :target: https://codecov.io/gh/emre/kaptan .. |license| image:: https://img.shields.io/github/license/emre/kaptan.svg :alt: License .. |docs| image:: https://readthedocs.org/projects/kaptan/badge/?version=latest :alt: Documentation Status :scale: 100% :target: https://readthedocs.org/projects/kaptan/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135300.0 kaptan-0.6.0/kaptan.egg-info/SOURCES.txt0000644000175000017500000000113114472630704015162 0ustar00ttAUTHORS LICENSE MANIFEST.in README.rst setup.py kaptan/__about__.py kaptan/__init__.py kaptan/__main__.py kaptan/_compat.py kaptan.egg-info/PKG-INFO kaptan.egg-info/SOURCES.txt kaptan.egg-info/dependency_links.txt kaptan.egg-info/entry_points.txt kaptan.egg-info/requires.txt kaptan.egg-info/top_level.txt kaptan/handlers/__init__.py kaptan/handlers/dict_handler.py kaptan/handlers/ini_handler.py kaptan/handlers/json_handler.py kaptan/handlers/pyfile_handler.py kaptan/handlers/yaml_handler.py requirements/base.txt requirements/dev.txt requirements/doc.txt requirements/test.txt tests/test_kaptan.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135300.0 kaptan-0.6.0/kaptan.egg-info/dependency_links.txt0000644000175000017500000000000114472630704017350 0ustar00tt ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135300.0 kaptan-0.6.0/kaptan.egg-info/entry_points.txt0000644000175000017500000000004714472630704016601 0ustar00tt[console_scripts] kaptan = kaptan:main ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135300.0 kaptan-0.6.0/kaptan.egg-info/requires.txt0000644000175000017500000000001514472630704015676 0ustar00ttPyYAML>=3.13 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135300.0 kaptan-0.6.0/kaptan.egg-info/top_level.txt0000644000175000017500000000000714472630704016031 0ustar00ttkaptan ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/requirements/0000755000175000017500000000000014472630705013056 5ustar00tt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693135078.0 kaptan-0.6.0/requirements/base.txt0000644000175000017500000000001514472630346014526 0ustar00ttPyYAML>=3.13 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693132051.0 kaptan-0.6.0/requirements/dev.txt0000644000175000017500000000001514472622423014367 0ustar00ttisort flake8 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693132051.0 kaptan-0.6.0/requirements/doc.txt0000644000175000017500000000005014472622423014355 0ustar00ttsphinx sphinx-rtd-theme sphinx-argparse ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693134295.0 kaptan-0.6.0/requirements/test.txt0000644000175000017500000000003514472626727014605 0ustar00ttpytest>=4.4.1 exceptiongroup ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/setup.cfg0000644000175000017500000000004614472630705012154 0ustar00tt[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693134092.0 kaptan-0.6.0/setup.py0000644000175000017500000000407414472626414012054 0ustar00tt""" kaptan ====== :copyright: (c) 2013- authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ import sys from setuptools import find_packages, setup from setuptools.command.test import test as TestCommand about = {} with open("kaptan/__about__.py") as fp: exec(fp.read(), about) with open('requirements/base.txt') as f: install_reqs = [line for line in f.read().split('\n') if line] with open('requirements/test.txt') as f: tests_reqs = [line for line in f.read().split('\n') if line] if sys.version_info[0] > 2: readme = open('README.rst', encoding='utf-8').read() else: readme = open('README.rst').read() class PyTest(TestCommand): user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")] def initialize_options(self): TestCommand.initialize_options(self) self.pytest_args = [] def run_tests(self): import pytest errno = pytest.main(self.pytest_args) sys.exit(errno) setup( name=about['__title__'], version=about['__version__'], packages=find_packages(), url=about['__url__'], license=about['__license__'], author=about['__author__'], author_email=about['__email__'], description=about['__description__'], long_description=readme, install_requires=install_reqs, tests_require=tests_reqs, cmdclass={'test': PyTest}, entry_points=dict( console_scripts=[ 'kaptan = kaptan:main', ], ), classifiers=[ 'Development Status :: 5 - Production/Stable', "Operating System :: POSIX", "Operating System :: MacOS :: MacOS X", 'Intended Audience :: Developers', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', ], ) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1693135300.5741932 kaptan-0.6.0/tests/0000755000175000017500000000000014472630705011475 5ustar00tt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1693047232.0 kaptan-0.6.0/tests/test_kaptan.py0000644000175000017500000001746514472354700014377 0ustar00tt# -*- coding: utf8 -*- """ kaptan ~~~~~~ :copyright: (c) 2013 by the authors and contributors (See AUTHORS file). :license: BSD, see LICENSE for more details. """ from __future__ import print_function, unicode_literals import json import os import os.path import sys import tempfile import pytest import kaptan try: import yaml except ImportError: yaml = None # python 2/3 compat try: unicode = unicode except NameError: # py changes unicode => str unicode = str PY2 = sys.version_info[0] == 2 sentinel = object() @pytest.fixture def testconfig(): return { 'debug': False, } def test_configuration_data(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) assert 'debug' in config.configuration_data def test_main_get(): config = kaptan.Kaptan() config.import_config({ 'show_comments': True, 'entry_count': 10, }) assert config.get("entry_count", 25) == 10 assert config.get("entry_count") == 10 assert config.get("show_comments", None) assert config.get("show_comments", False) def test_nested_configuration(): config = kaptan.Kaptan() config.import_config({ 'pagination': { 'per_page': 20, 'limit': 5, } }) assert config.get("pagination.limit") == 5 def test_lists_on_configuration(): config = kaptan.Kaptan() config.import_config({ 'servers': ['redis1', 'redis2', 'redis3'], }) assert config.get('servers.0') == 'redis1' def test_upsert(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) assert not config.get('debug') config.upsert('debug', True) assert config.get('debug') def test_default_dict_handler(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) assert not config.configuration_data['debug'] def test_json_handler(testconfig): config = kaptan.Kaptan(handler='json') config.import_config(json.dumps(testconfig)) assert not config.get('debug') def test_json_file_handler(tmpdir): json_file = tmpdir.join('config.json') json_file.write("""{"development": { "DATABASE_URI": "mysql://root:123456@localhost/posts" }, "production": { "DATABASE_URI": "mysql://poor_user:poor_password@localhost/poor_posts" } } """) config = kaptan.Kaptan(handler='json') config.import_config(str(json_file)) assert config.get( 'production.DATABASE_URI' ) == 'mysql://poor_user:poor_password@localhost/poor_posts' @pytest.mark.skipif(yaml is None or not PY2, reason='needs yaml') def test_yaml_safedump(testconfig): testdict = { unicode("development"): { "DATABASE_URI": "mysql://root:123456@localhost/posts" }, "production": { "DATABASE_URI": "mysql://poor_user:poor_password@localhost/poor_posts" # NOQA } } config = kaptan.Kaptan() config.import_config(testdict) yamlconfig = kaptan.Kaptan() yamlconfig.import_config(config.get()) assert '!!python/unicode' not in yamlconfig.export('yaml') assert '!!python/unicode' not in yamlconfig.export('yaml', safe=True) assert '!!python/unicode' in yamlconfig.export('yaml', safe=False) @pytest.mark.skipif(yaml is None, reason='needs yaml') def test_yaml_handler(testconfig): config = kaptan.Kaptan(handler='yaml') config.import_config(yaml.safe_dump(testconfig)) assert not config.get("debug") @pytest.mark.skipif(yaml is None, reason='needs yaml') def test_yaml_file_handler(tmpdir, testconfig): yaml_file = tmpdir.join('config.yaml') yaml_file.write(""" development: DATABASE_URI: mysql://root:123456@localhost/posts production: DATABASE_URI: mysql://poor_user:poor_password@localhost/poor_posts """) config = kaptan.Kaptan(handler='yaml') config.import_config(str(yaml_file)) assert config.get('production.DATABASE_URI') == \ 'mysql://poor_user:poor_password@localhost/poor_posts' @pytest.mark.skipif(yaml is None, reason='needs yaml') def test_yml_file_handler(tmpdir): yml_file = tmpdir.join('config.yml') yml_file.write(""" development: DATABASE_URI: mysql://root:123456@localhost/posts production: DATABASE_URI: mysql://poor_user:poor_password@localhost/poor_posts """) config = kaptan.Kaptan() config.import_config(str(yml_file)) assert config.get('production.DATABASE_URI') == \ 'mysql://poor_user:poor_password@localhost/poor_posts' def test_ini_handler(testconfig): value = """[development] DATABASE_URI = mysql://root:123456@localhost/posts [production] DATABASE_URI = mysql://poor_user:poor_password@localhost/poor_posts """ config = kaptan.Kaptan(handler='ini') config.import_config(value) assert config.get( 'production.database_uri' ) == 'mysql://poor_user:poor_password@localhost/poor_posts' def test_ini_file_handler(tmpdir, testconfig): ini_file = tmpdir.join('config.ini') ini_file.write("""[development] DATABASE_URI = mysql://root:123456@localhost/posts [production] DATABASE_URI = mysql://poor_user:poor_password@localhost/poor_posts """) config = kaptan.Kaptan(handler='ini') config.import_config(str(ini_file)) assert config.get( 'production.database_uri' ) == 'mysql://poor_user:poor_password@localhost/poor_posts' def test_ini_file_dump(tmpdir): testdict = { "development": { "DATABASE_URI": "mysql://root:123456@localhost/posts" }, "production": { "DATABASE_URI": "mysql://poor_user:poor_password@localhost/poor_posts" # NOQA } } config = kaptan.Kaptan() config.import_config(testdict) ini_file = tmpdir.join('config.ini') config.export('ini', file_=str(ini_file)) ini_config = kaptan.Kaptan(handler='ini') ini_config.import_config(str(ini_file)) assert ini_config.get( 'production.database_uri' ) == 'mysql://poor_user:poor_password@localhost/poor_posts' def test_py_file_handler(testconfig, tmpdir, monkeypatch): py_file = tmpdir.join('config.py') py_file.write("""DATABASE = 'mysql://root:123456@localhost/girlz' DEBUG = False PAGINATION = { 'per_page': 10, 'limit': 20, } """) monkeypatch.syspath_prepend(str(tmpdir)) normalize_name = str(py_file).rpartition('.')[0] config = kaptan.Kaptan(handler='file') config.import_config(normalize_name) assert config.get("PAGINATION.limit") == 20 def test_py_file_away_handler(tmpdir, testconfig): py_file = tmpdir.join('config2.py') py_file.write("""DATABASE = 'mysql://root:123456@localhost/girlz' DEBUG = False PAGINATION = { 'per_page': 10, 'limit': 20, } """) config = kaptan.Kaptan() config.import_config(str(py_file)) assert config.get("PAGINATION.limit") == 20 def test_py_file_away_noexist_raises(tmpdir, testconfig): py_file = tmpdir.join('config3.py') config = kaptan.Kaptan() with pytest.raises(IOError): config.import_config(str(py_file)) def test_invalid_key(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) with pytest.raises(KeyError): config.get('invalidkey') with pytest.raises(KeyError): config.get('invaliddict.invalidkey') def test_invalid_key_with_default(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) assert config.get('invalid_key', 'default_value') == 'default_value' assert config.get('invalid_key.bar.baz', 'default_value') == 'default_value' def test_default_value_none(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) assert config.get("invalid_key", None) is None assert config.get("invalid_key.bar.baz", None) is None def test_get_all_config(testconfig): config = kaptan.Kaptan() config.import_config(testconfig) assert isinstance(config.get(), dict) assert isinstance(config.get(''), dict)