setoptconf-0.2.0/0000755000076600000240000000000012313374174015520 5ustar jasonsimeonestaff00000000000000setoptconf-0.2.0/LICENSE0000644000076600000240000000206212264550663016531 0ustar jasonsimeonestaff00000000000000The MIT License Copyright (c)2014 Jason Simeone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. setoptconf-0.2.0/MANIFEST.in0000644000076600000240000000003412265061015017244 0ustar jasonsimeonestaff00000000000000include README.rst LICENSE setoptconf-0.2.0/PKG-INFO0000644000076600000240000000611612313374174016621 0ustar jasonsimeonestaff00000000000000Metadata-Version: 1.1 Name: setoptconf Version: 0.2.0 Summary: A module for retrieving program settings from various sources in a consistant method. Home-page: https://github.com/jayclassless/setoptconf Author: Jason Simeone Author-email: jay@classless.net License: MIT Description: ========== setoptconf ========== .. image:: https://travis-ci.org/jayclassless/setoptconf.svg?branch=master :target: https://travis-ci.org/jayclassless/setoptconf ``setoptconf`` is a Python library that can be used to retrieve program settings from a variety of common sources: * Command Line * Environment Variables * INI Files * JSON Files * YAML Files * Python Objects/Modules The goal of this project is to define your desired settings in a simple and consistent way, and then point setoptconf at as many of the sources as you'd like to use, and let it comb them all, looking for your settings. This README is admittedly very light on details. Full documentation will come in time. For now, here's an example of its use: Import the library:: import setoptconf as soc Instantiate the manager:: manager = soc.ConfigurationManager('myprogram') Define the settings we'd like to collect:: manager.add(soc.StringSetting('foo')) manager.add(soc.IntegerSetting('bar', required=True)) manager.add(soc.BooleanSetting('baz', default=True)) Retreive the settings from our desired sources, combining the settings and overriding with the priority implied by the order of the sources we pass:: config = manager.retrieve( # This source pulls from the command line using argparse. soc.CommandLineSource, # This source pulls from environment variables that are prefixed # with MYPROGRAM_* soc.EnvironmentVariableSource, # This source pulls from the named INI files. It stops at the first # file it finds. soc.ConfigFileSource(('.myprogramrc', '/etc/myprogram.conf')), ) We now have a Configuration object named ``config`` that has three attributes; ``foo``, ``bar``, and ``baz``. Keywords: settings,options,configuration,config,arguments Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Software Development :: Libraries :: Python Modules setoptconf-0.2.0/README.rst0000644000076600000240000000336212313367456017220 0ustar jasonsimeonestaff00000000000000========== setoptconf ========== .. image:: https://travis-ci.org/jayclassless/setoptconf.svg?branch=master :target: https://travis-ci.org/jayclassless/setoptconf ``setoptconf`` is a Python library that can be used to retrieve program settings from a variety of common sources: * Command Line * Environment Variables * INI Files * JSON Files * YAML Files * Python Objects/Modules The goal of this project is to define your desired settings in a simple and consistent way, and then point setoptconf at as many of the sources as you'd like to use, and let it comb them all, looking for your settings. This README is admittedly very light on details. Full documentation will come in time. For now, here's an example of its use: Import the library:: import setoptconf as soc Instantiate the manager:: manager = soc.ConfigurationManager('myprogram') Define the settings we'd like to collect:: manager.add(soc.StringSetting('foo')) manager.add(soc.IntegerSetting('bar', required=True)) manager.add(soc.BooleanSetting('baz', default=True)) Retreive the settings from our desired sources, combining the settings and overriding with the priority implied by the order of the sources we pass:: config = manager.retrieve( # This source pulls from the command line using argparse. soc.CommandLineSource, # This source pulls from environment variables that are prefixed # with MYPROGRAM_* soc.EnvironmentVariableSource, # This source pulls from the named INI files. It stops at the first # file it finds. soc.ConfigFileSource(('.myprogramrc', '/etc/myprogram.conf')), ) We now have a Configuration object named ``config`` that has three attributes; ``foo``, ``bar``, and ``baz``. setoptconf-0.2.0/setoptconf/0000755000076600000240000000000012313374174017704 5ustar jasonsimeonestaff00000000000000setoptconf-0.2.0/setoptconf/__init__.py0000644000076600000240000000027512313362770022020 0ustar jasonsimeonestaff00000000000000 # pylint: disable=W0401 from .config import * from .datatype import * from .exception import * from .manager import * from .setting import * from .source import * __version__ = '0.2.0' setoptconf-0.2.0/setoptconf/config.py0000644000076600000240000000462012266311640021521 0ustar jasonsimeonestaff00000000000000from .exception import MissingRequiredError, ReadOnlyError from .util import UnicodeMixin __all__ = ( 'Configuration', ) class Configuration(UnicodeMixin): def __init__(self, settings, parent=None): self.__dict__['_parent'] = parent self.__dict__['_settings'] = {} for setting in settings: self._settings[setting.name] = setting def validate_setting(self, name): if name in self._settings: setting = self._settings[name] if setting.required and not setting.established: if self._parent: self._parent.validate_setting(name) else: raise MissingRequiredError(name) elif self._parent: self._parent.validate_setting(name) else: raise AttributeError('No such setting "%s"' % name) def validate(self): for name in self: self.validate_setting(name) def __getattr__(self, name): if name in self._settings: if self._settings[name].established: return self._settings[name].value elif self._parent: return getattr(self._parent, name) else: return self._settings[name].default elif self._parent: return getattr(self._parent, name) else: raise AttributeError('No such setting "%s"' % name) def __getitem__(self, key): return getattr(self, key) def __setattr__(self, name, value): raise ReadOnlyError('Cannot change the value of settings') def __setitem__(self, key, value): setattr(self, key, value) def __delattr__(self, name): raise ReadOnlyError('Cannot delete settings') def __delitem__(self, key): delattr(self, key) def __iter__(self): all_names = set(self._settings.keys()) if self._parent: all_names.update(iter(self._parent)) return iter(all_names) def __len__(self): return len(list(iter(self))) def __contains__(self, item): return item in list(iter(self)) def __unicode__(self): # pragma: no cover return u'Configuration(%s)' % ( u', '.join([ u'%s=%s' % (name, repr(self[name])) for name in self ]) ) def __repr__(self): # pragma: no cover return '<%s>' % str(self) setoptconf-0.2.0/setoptconf/datatype.py0000644000076600000240000000642212313362426022072 0ustar jasonsimeonestaff00000000000000from .exception import DataTypeError __all__ = ( 'DataType', 'String', 'Integer', 'Float', 'Boolean', 'List', 'Choice', ) class DataType(object): def sanitize(self, value): raise NotImplementedError() def is_valid(self, value): try: self.sanitize(value) except DataTypeError: return False else: return True class String(DataType): def sanitize(self, value): if value is not None: value = unicode(value) return value class Integer(DataType): def sanitize(self, value): if value is not None: try: value = int(value) except: raise DataTypeError('"%s" is not valid Integer' % value) return value class Float(DataType): def sanitize(self, value): if value is not None: try: value = float(value) except: raise DataTypeError('"%s" is not valid Float' % value) return value class Boolean(DataType): TRUTHY_STRINGS = ('Y', 'YES', 'T', 'TRUE', 'ON', '1') FALSY_STRINGS = ('', 'N', 'NO', 'F', 'FALSE', 'OFF', '0') def sanitize(self, value): if value is None or isinstance(value, bool): return value if isinstance(value, (int, long)): return True if value else False if isinstance(value, basestring) and value: value = value.strip().upper() if value in self.TRUTHY_STRINGS: return True elif value in self.FALSY_STRINGS: return False else: raise DataTypeError( 'Could not coerce "%s" to a Boolean' % ( value, ) ) return True if value else False class List(DataType): def __init__(self, subtype): super(List, self).__init__() if isinstance(subtype, DataType): self.subtype = subtype elif isinstance(subtype, type) and issubclass(subtype, DataType): self.subtype = subtype() else: raise TypeError('subtype must be a DataType') def sanitize(self, value): if value is None: return value if not isinstance(value, (list, tuple)): value = [value] value = [ self.subtype.sanitize(v) for v in value ] return value class Choice(DataType): def __init__(self, choices, subtype=None): super(Choice, self).__init__() subtype = subtype or String() if isinstance(subtype, DataType): self.subtype = subtype elif isinstance(subtype, type) and issubclass(subtype, DataType): self.subtype = subtype() else: raise TypeError('subtype must be a DataType') self.choices = choices def sanitize(self, value): if value is None: return value value = self.subtype.sanitize(value) if value not in self.choices: raise DataTypeError( '"%s" is not one of (%s)' % ( value, ', '.join([repr(c) for c in self.choices]), ) ) return value setoptconf-0.2.0/setoptconf/exception.py0000644000076600000240000000056512265053125022256 0ustar jasonsimeonestaff00000000000000 __all__ = ( 'SetOptConfError', 'NamingError', 'DataTypeError', 'MissingRequiredError', 'ReadOnlyError', ) class SetOptConfError(Exception): pass class NamingError(SetOptConfError): pass class DataTypeError(SetOptConfError): pass class MissingRequiredError(SetOptConfError): pass class ReadOnlyError(SetOptConfError): pass setoptconf-0.2.0/setoptconf/manager.py0000644000076600000240000000221112265053113021655 0ustar jasonsimeonestaff00000000000000from copy import deepcopy from .config import Configuration from .setting import Setting from .source.base import Source __all__ = ( 'ConfigurationManager', ) class ConfigurationManager(object): def __init__(self, name): self.name = name self.settings = [] def add(self, setting): if isinstance(setting, Setting): self.settings.append(setting) else: raise TypeError('Can only add objects of type Setting') def retrieve(self, *sources): to_process = [] for source in reversed(sources): if isinstance(source, Source): to_process.append(source) elif isinstance(source, type) and issubclass(source, Source): to_process.append(source()) else: raise TypeError('All sources must be a Source') config = Configuration(settings=self.settings) for source in to_process: config = source.get_config( deepcopy(self.settings), manager=self, parent=config, ) config.validate() return config setoptconf-0.2.0/setoptconf/setting.py0000644000076600000240000000333712313366545021744 0ustar jasonsimeonestaff00000000000000 # pylint: disable=W0401,W0223 import re from .datatype import * from .exception import NamingError from .util import UnicodeMixin __all__ = ( 'Setting', 'StringSetting', 'IntegerSetting', 'FloatSetting', 'BooleanSetting', 'ListSetting', 'ChoiceSetting', ) class Setting(UnicodeMixin, DataType): RE_NAME = re.compile(r'^[a-z](?:[a-z0-9]|[_](?![_]))*[a-z0-9]$') def __init__(self, name, default=None, required=False): if Setting.RE_NAME.match(name): self.name = name else: raise NamingError(name) self._value = None self.default = self.sanitize(default) self.required = required self.established = False @property def value(self): return self._value @value.setter def value(self, value): self._value = self.sanitize(value) self.established = True def __unicode__(self): # pragma: no cover return unicode(self.name) def __repr__(self): # pragma: no cover return '<%s(%s=%s)>' % ( self.__class__.__name__, self.name, self.value if self.established else '', ) class StringSetting(Setting, String): pass class IntegerSetting(Setting, Integer): pass class FloatSetting(Setting, Float): pass class BooleanSetting(Setting, Boolean): pass class ListSetting(Setting, List): def __init__(self, name, subtype, **kwargs): List.__init__(self, subtype) Setting.__init__(self, name, **kwargs) class ChoiceSetting(Setting, Choice): def __init__(self, name, choices, subtype=None, **kwargs): Choice.__init__(self, choices, subtype=subtype) Setting.__init__(self, name, **kwargs) setoptconf-0.2.0/setoptconf/source/0000755000076600000240000000000012313374174021204 5ustar jasonsimeonestaff00000000000000setoptconf-0.2.0/setoptconf/source/__init__.py0000644000076600000240000000052212266315333023313 0ustar jasonsimeonestaff00000000000000 # pylint: disable=W0401 from .base import * from .commandline import * from .configfile import * from .environment import * from .filebased import * from .jsonfile import * from .mapping import * from .modobj import * try: import yaml except ImportError: # pragma: no cover pass else: del yaml from .yamlfile import * setoptconf-0.2.0/setoptconf/source/base.py0000644000076600000240000000023012265053130022453 0ustar jasonsimeonestaff00000000000000 __all__ = ( 'Source', ) class Source(object): def get_config(self, settings, manager=None, parent=None): raise NotImplementedError() setoptconf-0.2.0/setoptconf/source/commandline.py0000644000076600000240000001205412313363704024043 0ustar jasonsimeonestaff00000000000000import argparse import shlex import sys from copy import deepcopy from ..config import Configuration from ..setting import BooleanSetting, ChoiceSetting, ListSetting from .base import Source __all__ = ( 'CommandLineSource', ) # pylint: disable=R0201 class CommandLineSource(Source): # pylint: disable=R0913 def __init__( self, arguments=None, options=None, version=None, parser_options=None, positional=None): super(CommandLineSource, self).__init__() if arguments is None: self.arguments = sys.argv[1:] elif isinstance(arguments, basestring): self.arguments = shlex.split(arguments) elif isinstance(arguments, (list, tuple)): self.arguments = arguments else: raise TypeError('arguments must be a string or list of strings') self.version = version self.options = options or {} self.parser_options = parser_options or {} self.positional = positional or () def get_flags(self, setting): if setting.name in self.options: if 'flags' in self.options[setting.name]: return self.options[setting.name]['flags'] flags = [] flag = '--%s' % setting.name.lower().replace('_', '-') flags.append(flag) return flags def get_action(self, setting): if isinstance(setting, BooleanSetting): return 'store_false' if setting.default else 'store_true' elif isinstance(setting, ListSetting): return 'append' else: return 'store' # pylint: disable=W0613 def get_default(self, setting): # Caveat: Returning something other than SUPPRESS probably won't # work the way you'd think. return argparse.SUPPRESS def get_type(self, setting): if isinstance(setting, (ListSetting, BooleanSetting)): return None elif isinstance(setting, ChoiceSetting): return setting.subtype.sanitize else: return setting.sanitize def get_dest(self, setting): return setting.name def get_choices(self, setting): if isinstance(setting, ChoiceSetting): return setting.choices else: return None def get_help(self, setting): if setting.name in self.options: if 'help' in self.options[setting.name]: return self.options[setting.name]['help'] return None def get_metavar(self, setting): if setting.name in self.options: if 'metavar' in self.options[setting.name]: return self.options[setting.name]['metavar'] return None def build_argument(self, setting): flags = self.get_flags(setting) action = self.get_action(setting) default = self.get_default(setting) argtype = self.get_type(setting) dest = self.get_dest(setting) choices = self.get_choices(setting) arghelp = self.get_help(setting) metavar = self.get_metavar(setting) argument_kwargs = { 'action': action, 'default': default, 'dest': dest, 'help': arghelp, } if argtype: argument_kwargs['type'] = argtype if choices: argument_kwargs['choices'] = choices if metavar: argument_kwargs['metavar'] = metavar return flags, argument_kwargs def build_parser(self, settings, manager): parser_options = deepcopy(self.parser_options) if not parser_options.get('prog') and manager: parser_options['prog'] = manager.name parser = argparse.ArgumentParser(**parser_options) add_version = (self.version is not None) for setting in settings: flags, argument_kwargs = self.build_argument(setting) parser.add_argument(*flags, **argument_kwargs) if add_version and setting.name == 'version': # Don't want to conflict with the desired setting add_version = False if add_version: parser.add_argument( '--version', action='version', version='%(prog)s ' + self.version, ) if self.positional: for name, options in self.positional: parser.add_argument(name, **options) return parser def get_config(self, settings, manager=None, parent=None): parser = self.build_parser(settings, manager) parsed = parser.parse_args(self.arguments) for setting in settings: if hasattr(parsed, setting.name): setting.value = getattr(parsed, setting.name) if self.positional and manager: arguments = {} for name, _ in self.positional: if hasattr(parsed, name): arguments[name] = getattr(parsed, name) setattr(manager, 'arguments', arguments) return Configuration(settings=settings, parent=parent) setoptconf-0.2.0/setoptconf/source/configfile.py0000644000076600000240000000172312265053171023663 0ustar jasonsimeonestaff00000000000000import ConfigParser from ..setting import ListSetting from ..util import csv_to_list from .filebased import FileBasedSource __all__ = ( 'ConfigFileSource', ) class ConfigFileSource(FileBasedSource): def __init__(self, *args, **kwargs): self.section = kwargs.pop('section', None) super(ConfigFileSource, self).__init__(*args, **kwargs) def get_settings_from_file(self, file_path, settings, manager=None): section = self.section or manager.name.lower() parser = ConfigParser.ConfigParser() parser.read(file_path) if not parser.has_section(section): return None for setting in settings: if parser.has_option(section, setting.name): opt = parser.get(section, setting.name) if isinstance(setting, ListSetting): setting.value = csv_to_list(opt) else: setting.value = opt return settings setoptconf-0.2.0/setoptconf/source/environment.py0000644000076600000240000000175712265053136024132 0ustar jasonsimeonestaff00000000000000import os from ..config import Configuration from ..setting import ListSetting from ..util import csv_to_list from .base import Source __all__ = ( 'EnvironmentVariableSource', ) class EnvironmentVariableSource(Source): def __init__(self, prefix=None): super(EnvironmentVariableSource, self).__init__() self.prefix = prefix def get_config(self, settings, manager=None, parent=None): if manager and not self.prefix: self.prefix = manager.name for setting in settings: self.get_setting(setting) return Configuration(settings=settings, parent=parent) def get_setting(self, setting): name = setting.name if self.prefix: name = '%s_%s' % (self.prefix, name) name = name.upper() if name in os.environ: if isinstance(setting, ListSetting): setting.value = csv_to_list(os.environ[name]) else: setting.value = os.environ[name] setoptconf-0.2.0/setoptconf/source/filebased.py0000644000076600000240000000554712301500077023475 0ustar jasonsimeonestaff00000000000000import os.path from copy import deepcopy from ..config import Configuration from .base import Source __all__ = ( 'HomeDirectory', 'ConfigDirectory', 'FileBasedSource', ) class DirectoryModifier(object): def __init__(self, target_file): self.target_file = target_file def __call__(self): raise NotImplementedError() class HomeDirectory(DirectoryModifier): def __call__(self): return os.path.expanduser( os.path.join( '~', self.target_file, ) ) class ConfigDirectory(DirectoryModifier): def __call__(self): config_dir = os.getenv('XDG_CONFIG_HOME') \ or os.path.expanduser( os.path.join( '~', '.config', ) ) return os.path.join(config_dir, self.target_file) class FileBasedSource(Source): def __init__(self, files, base_path=None, combine=False): super(FileBasedSource, self).__init__() if isinstance(files, (basestring, DirectoryModifier)): files = [files] elif not isinstance(files, (tuple, list)): raise TypeError('files must be a string or list of strings') self.files = [] for target in files: if isinstance(target, basestring): self.files.append(target) elif isinstance(target, DirectoryModifier): self.files.append(target()) else: raise TypeError('files must be a string or list of strings') self.base_path = base_path or os.getcwd() self.combine = combine def get_config(self, settings, manager=None, parent=None): parsed_settings = [] for file_source in self.files: if os.path.isabs(file_source): file_path = file_source else: file_path = os.path.join(self.base_path, file_source) if os.path.exists(file_path): file_settings = self.get_settings_from_file( file_path, deepcopy(settings), manager=manager, ) if file_settings: parsed_settings.append(file_settings) if not self.combine: # No need to gather any more, we only want one. break if parsed_settings: config = parent for parsed_setting in reversed(parsed_settings): config = Configuration( settings=parsed_setting, parent=config, ) else: config = Configuration(settings=settings, parent=parent) return config def get_settings_from_file(self, file_path, settings, manager=None): raise NotImplementedError() setoptconf-0.2.0/setoptconf/source/jsonfile.py0000644000076600000240000000152512266320351023365 0ustar jasonsimeonestaff00000000000000import codecs import json from .filebased import FileBasedSource __all__ = ( 'JsonFileSource', ) class JsonFileSource(FileBasedSource): def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') super(JsonFileSource, self).__init__(*args, **kwargs) def get_settings_from_file(self, file_path, settings, manager=None): content = codecs.open(file_path, 'r', self.encoding).read().strip() if not content: return None content = json.loads(content) if not content: return None if not isinstance(content, dict): raise TypeError('JSON files must contain only objects') for setting in settings: if setting.name in content: setting.value = content[setting.name] return settings setoptconf-0.2.0/setoptconf/source/mapping.py0000644000076600000240000000076212265053134023212 0ustar jasonsimeonestaff00000000000000from ..config import Configuration from .base import Source __all__ = ( 'MappingSource', ) class MappingSource(Source): def __init__(self, target): super(MappingSource, self).__init__() self.target = target def get_config(self, settings, manager=None, parent=None): for setting in settings: if setting.name in self.target: setting.value = self.target[setting.name] return Configuration(settings=settings, parent=parent) setoptconf-0.2.0/setoptconf/source/modobj.py0000644000076600000240000000337112265053132023026 0ustar jasonsimeonestaff00000000000000import types from ..config import Configuration from .base import Source __all__ = ( 'ModuleSource', 'ObjectSource', ) class ModuleSource(Source): def __init__(self, target): super(ModuleSource, self).__init__() if isinstance(target, types.ModuleType): self.target = target elif isinstance(target, basestring): self.target = __import__(target, globals(), locals(), [], -1) else: raise TypeError( 'target must be a Module or a String naming a Module' ) def get_config(self, settings, manager=None, parent=None): for setting in settings: if hasattr(self.target, setting.name): setting.value = getattr(self.target, setting.name) return Configuration(settings=settings, parent=parent) class ObjectSource(Source): def __init__(self, target): super(ObjectSource, self).__init__() if isinstance(target, (type, object)): self.target = target elif isinstance(target, basestring): parts = target.rsplit('.', 2) if len(parts) == 2: mod = parts[0] fromlist = [parts[1]] else: mod = parts[0] fromlist = [] self.target = __import__(mod, globals(), locals(), fromlist, -1) else: raise TypeError( 'target must be an Object or a String naming an Object' ) def get_config(self, settings, manager=None, parent=None): for setting in settings: if hasattr(self.target, setting.name): setting.value = getattr(self.target, setting.name) return Configuration(settings=settings, parent=parent) setoptconf-0.2.0/setoptconf/source/yamlfile.py0000644000076600000240000000153312266320355023361 0ustar jasonsimeonestaff00000000000000import codecs import yaml from .filebased import FileBasedSource __all__ = ( 'YamlFileSource', ) class YamlFileSource(FileBasedSource): def __init__(self, *args, **kwargs): self.encoding = kwargs.pop('encoding', 'utf-8') super(YamlFileSource, self).__init__(*args, **kwargs) def get_settings_from_file(self, file_path, settings, manager=None): content = codecs.open(file_path, 'r', self.encoding).read().strip() if not content: return None content = yaml.safe_load(content) if not content: return None if not isinstance(content, dict): raise TypeError('YAML files must contain only mappings') for setting in settings: if setting.name in content: setting.value = content[setting.name] return settings setoptconf-0.2.0/setoptconf/util.py0000644000076600000240000000106212266320021021217 0ustar jasonsimeonestaff00000000000000import csv import StringIO import sys __all__ = ( 'csv_to_list', 'UnicodeMixin', ) def csv_to_list(value): if isinstance(value, basestring) and value: reader = csv.reader(StringIO.StringIO(value)) parsed = next(reader) return parsed return [] # Adapted from http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/ # pylint: disable=R0903 class UnicodeMixin(object): if sys.version_info >= (3, 0): __str__ = lambda x: x.__unicode__() else: __str__ = lambda x: unicode(x).encode('utf-8') setoptconf-0.2.0/setoptconf.egg-info/0000755000076600000240000000000012313374174021376 5ustar jasonsimeonestaff00000000000000setoptconf-0.2.0/setoptconf.egg-info/dependency_links.txt0000644000076600000240000000000112313374174025444 0ustar jasonsimeonestaff00000000000000 setoptconf-0.2.0/setoptconf.egg-info/PKG-INFO0000644000076600000240000000611612313374174022477 0ustar jasonsimeonestaff00000000000000Metadata-Version: 1.1 Name: setoptconf Version: 0.2.0 Summary: A module for retrieving program settings from various sources in a consistant method. Home-page: https://github.com/jayclassless/setoptconf Author: Jason Simeone Author-email: jay@classless.net License: MIT Description: ========== setoptconf ========== .. image:: https://travis-ci.org/jayclassless/setoptconf.svg?branch=master :target: https://travis-ci.org/jayclassless/setoptconf ``setoptconf`` is a Python library that can be used to retrieve program settings from a variety of common sources: * Command Line * Environment Variables * INI Files * JSON Files * YAML Files * Python Objects/Modules The goal of this project is to define your desired settings in a simple and consistent way, and then point setoptconf at as many of the sources as you'd like to use, and let it comb them all, looking for your settings. This README is admittedly very light on details. Full documentation will come in time. For now, here's an example of its use: Import the library:: import setoptconf as soc Instantiate the manager:: manager = soc.ConfigurationManager('myprogram') Define the settings we'd like to collect:: manager.add(soc.StringSetting('foo')) manager.add(soc.IntegerSetting('bar', required=True)) manager.add(soc.BooleanSetting('baz', default=True)) Retreive the settings from our desired sources, combining the settings and overriding with the priority implied by the order of the sources we pass:: config = manager.retrieve( # This source pulls from the command line using argparse. soc.CommandLineSource, # This source pulls from environment variables that are prefixed # with MYPROGRAM_* soc.EnvironmentVariableSource, # This source pulls from the named INI files. It stops at the first # file it finds. soc.ConfigFileSource(('.myprogramrc', '/etc/myprogram.conf')), ) We now have a Configuration object named ``config`` that has three attributes; ``foo``, ``bar``, and ``baz``. Keywords: settings,options,configuration,config,arguments Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.1 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Software Development :: Libraries :: Python Modules setoptconf-0.2.0/setoptconf.egg-info/requires.txt0000644000076600000240000000001712313374174023774 0ustar jasonsimeonestaff00000000000000 [YAML] pyyamlsetoptconf-0.2.0/setoptconf.egg-info/SOURCES.txt0000644000076600000240000000151712313374174023266 0ustar jasonsimeonestaff00000000000000LICENSE MANIFEST.in README.rst setup.cfg setup.py setoptconf/__init__.py setoptconf/config.py setoptconf/datatype.py setoptconf/exception.py setoptconf/manager.py setoptconf/setting.py setoptconf/util.py setoptconf.egg-info/PKG-INFO setoptconf.egg-info/SOURCES.txt setoptconf.egg-info/dependency_links.txt setoptconf.egg-info/requires.txt setoptconf.egg-info/top_level.txt setoptconf/source/__init__.py setoptconf/source/base.py setoptconf/source/commandline.py setoptconf/source/configfile.py setoptconf/source/environment.py setoptconf/source/filebased.py setoptconf/source/jsonfile.py setoptconf/source/mapping.py setoptconf/source/modobj.py setoptconf/source/yamlfile.py test/test_configuration.py test/test_datatypes.py test/test_directory_modifiers.py test/test_file_sources.py test/test_manager.py test/test_settings.py test/test_sources.pysetoptconf-0.2.0/setoptconf.egg-info/top_level.txt0000644000076600000240000000001312313374174024122 0ustar jasonsimeonestaff00000000000000setoptconf setoptconf-0.2.0/setup.cfg0000644000076600000240000000045412313374174017344 0ustar jasonsimeonestaff00000000000000[nosy] base_path = ./ glob_patterns = *.py [nosetests] with-coverage = 1 cover-package = setoptconf cover-erase = 1 nocapture = 1 [prospector] strictness = veryhigh profiles = .prospector.yaml output_format = grouped messages_only = True [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 setoptconf-0.2.0/setup.py0000644000076600000240000000364512267622434017245 0ustar jasonsimeonestaff00000000000000import os.path import re import sys from setuptools import setup, find_packages PYVER = sys.version_info SETUP_KWARGS = {} DEPENDENCIES = [] # Do we need to install argparse? if PYVER < (2, 7): DEPENDENCIES.append('argparse') elif PYVER >= (3, 0) and PYVER < (3, 2): DEPENDENCIES.append('argparse') # Are we on Py3K? if PYVER >= (3, 0): SETUP_KWARGS['use_2to3'] = True def get_version(): init = os.path.join(os.path.dirname(__file__), 'setoptconf/__init__.py') source = open(init, 'r').read() version = re.search( r"__version__ = '(?P[^']+)'", source, ).group('version') return version def get_description(): readme = os.path.join(os.path.dirname(__file__), 'README.rst') return open(readme, 'r').read() setup( name='setoptconf', version=get_version(), author='Jason Simeone', author_email='jay@classless.net', license='MIT', keywords=['settings', 'options', 'configuration', 'config', 'arguments'], description='A module for retrieving program settings from various' ' sources in a consistant method.', long_description=get_description(), url='https://github.com/jayclassless/setoptconf', classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.1', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Topic :: Software Development :: Libraries :: Python Modules' ], packages=find_packages(exclude=['test.*', 'test']), install_requires=DEPENDENCIES, extras_require={ 'YAML': ['pyyaml'], }, **SETUP_KWARGS ) setoptconf-0.2.0/test/0000755000076600000240000000000012313374174016477 5ustar jasonsimeonestaff00000000000000setoptconf-0.2.0/test/test_configuration.py0000644000076600000240000000767012266312700022763 0ustar jasonsimeonestaff00000000000000 import setoptconf as soc def make_settings1(): settings = [] setting = soc.StringSetting('foo') setting.value = 'hello' settings.append(setting) setting = soc.IntegerSetting('bar') setting.value = 123 settings.append(setting) setting = soc.BooleanSetting('baz', default=False) settings.append(setting) return settings def make_settings2(): settings = [] setting = soc.StringSetting('foo') setting.value = 'goodbye' settings.append(setting) setting = soc.IntegerSetting('bar') settings.append(setting) return settings def make_settings3(): settings = [] setting = soc.StringSetting('foo') setting.value = 'happy' settings.append(setting) return settings def test_one_level(): config = soc.Configuration(make_settings1()) assert len(config) == 3 assert 'foo' in config assert config.foo == 'hello' assert config['foo'] == 'hello' assert 'bar' in config assert config.bar == 123 assert config['bar'] == 123 assert 'baz' in config assert config.baz is False assert config['baz'] is False def test_two_level(): parent = soc.Configuration(make_settings1()) child = soc.Configuration(make_settings2(), parent=parent) assert len(child) == 3 assert 'foo' in child assert child.foo == 'goodbye' assert child['foo'] == 'goodbye' assert 'bar' in child assert child.bar == 123 assert child['bar'] == 123 assert 'baz' in child assert child.baz is False assert child['baz'] is False def test_three_level(): grand_parent = soc.Configuration(make_settings1()) parent = soc.Configuration(make_settings2(), parent=grand_parent) child = soc.Configuration(make_settings3(), parent=parent) assert len(child) == 3 assert 'foo' in child assert child.foo == 'happy' assert child['foo'] == 'happy' assert 'bar' in child assert child.bar == 123 assert child['bar'] == 123 assert 'baz' in child assert child.baz is False assert child['baz'] is False def test_missing(): config = soc.Configuration(make_settings1()) try: config['happy'] except AttributeError: pass else: assert False, 'No AttributeError for missing setting' try: config.happy except AttributeError: pass else: assert False, 'No AttributeError for missing setting' try: config.validate_setting('happy') except AttributeError: pass else: assert False, 'No AttributeError for missing setting' def test_validation(): settings1 = make_settings1() parent = soc.Configuration(settings1) parent.validate() child = soc.Configuration(make_settings2(), parent=parent) child.validate() setting = soc.StringSetting('happy', required=True) settings1.append(setting) parent = soc.Configuration(settings1) child = soc.Configuration(make_settings2(), parent=parent) try: parent.validate() except soc.MissingRequiredError: pass else: assert False, 'No MissingRequiredError for required setting' try: child.validate() except soc.MissingRequiredError: pass else: assert False, 'No MissingRequiredError for required setting' setting.value = 'sad' parent.validate() child.validate() def test_readonly(): config = soc.Configuration(make_settings1()) try: config.foo = 'qwerty' except soc.ReadOnlyError: pass else: assert False, 'Expected ReadOnlyError' try: config['foo'] = 'qwerty' except soc.ReadOnlyError: pass else: assert False, 'Expected ReadOnlyError' try: del config.foo except soc.ReadOnlyError: pass else: assert False, 'Expected ReadOnlyError' try: del config['foo'] except soc.ReadOnlyError: pass else: assert False, 'Expected ReadOnlyError' setoptconf-0.2.0/test/test_datatypes.py0000644000076600000240000001052412313362502022100 0ustar jasonsimeonestaff00000000000000from decimal import Decimal import setoptconf as soc GOOD_SIMPLE_VALUES = ( (soc.String, None, None), (soc.String, 'foo', 'foo'), (soc.String, '1', '1'), (soc.String, 1, '1'), (soc.String, 1.23, '1.23'), (soc.String, Decimal('1.23'), '1.23'), (soc.Integer, None, None), (soc.Integer, 123, 123), (soc.Integer, '123', 123), (soc.Integer, 123.45, 123), (soc.Integer, Decimal('123'), 123), (soc.Integer, Decimal('123.45'), 123), (soc.Float, None, None), (soc.Float, 123, 123.0), (soc.Float, '123', 123.0), (soc.Float, 123.45, 123.45), (soc.Float, Decimal('123'), 123.0), (soc.Float, Decimal('123.45'), 123.45), (soc.Boolean, None, None), (soc.Boolean, True, True), (soc.Boolean, False, False), (soc.Boolean, 'y', True), (soc.Boolean, 'yes', True), (soc.Boolean, 't', True), (soc.Boolean, 'true', True), (soc.Boolean, 'on', True), (soc.Boolean, '1', True), (soc.Boolean, '', False), (soc.Boolean, 'n', False), (soc.Boolean, 'no', False), (soc.Boolean, 'f', False), (soc.Boolean, 'false', False), (soc.Boolean, 'off', False), (soc.Boolean, '0', False), (soc.Boolean, 123, True), (soc.Boolean, 0, False), (soc.Boolean, 123.45, True), ) BAD_SIMPLE_VALUES = ( (soc.Integer, 'foo'), (soc.Integer, '123abc'), (soc.Float, 'foo'), (soc.Float, '123abc'), (soc.Float, '123.45abc'), (soc.Boolean, 'foo'), ) def test_simple_sanitization(): for datatype, in_value, out_value in GOOD_SIMPLE_VALUES: yield check_good_value, datatype, in_value, out_value for datatype, in_value in BAD_SIMPLE_VALUES: yield check_bad_value, datatype, in_value def check_good_value(datatype, in_value, out_value): dt = datatype() assert dt.sanitize(in_value) == out_value assert dt.is_valid(in_value) is True def check_bad_value(datatype, in_value): dt = datatype() try: dt.sanitize(in_value) except soc.DataTypeError: pass else: assert False, 'Invalid %s allowed: %s' % ( datatype.__name__, in_value, ) assert dt.is_valid(in_value) is False GOOD_LIST_VALUES = ( (soc.String, None, None), (soc.String, [], []), (soc.String, ['foo', 'bar'], ['foo', 'bar']), (soc.String, ('foo', 'bar'), ['foo', 'bar']), (soc.String(), ['foo', 'bar'], ['foo', 'bar']), (soc.String, 'foo', ['foo']), (soc.Integer, [123, '456'], [123, 456]), ) BAD_LIST_VALUES = ( (soc.Integer, ['foo'], soc.DataTypeError), (soc.Boolean, [True, False, 'y', 4, 'foo'], soc.DataTypeError), ('a', ['foo'], TypeError), (soc.Configuration, ['foo'], TypeError), ) def test_list_sanitization(): for subtype, in_value, out_value in GOOD_LIST_VALUES: yield check_good_list_value, subtype, in_value, out_value for subtype, in_value, exc in BAD_LIST_VALUES: yield check_bad_list_value, subtype, in_value, exc def check_good_list_value(subtype, in_value, out_value): dt = soc.List(subtype) assert dt.sanitize(in_value) == out_value def check_bad_list_value(subtype, in_value, exc): try: dt = soc.List(subtype) dt.sanitize(in_value) except exc: pass else: assert False, 'Invalid %s allowed: %s' % ( subtype.__class__.__name__, in_value, ) GOOD_CHOICE_VALUES = ( (soc.String, ['foo', 'bar'], None), (soc.String, ['foo', 'bar'], 'foo'), (None, ['foo', 'bar'], 'foo'), (soc.Integer, [1,2,3], 2), (soc.Integer(), [1,2,3], 2), ) BAD_CHOICE_VALUES = ( (soc.String, ['foo', 'bar'], 'baz', soc.DataTypeError), (soc.String, [1, 2, 3], 'baz', soc.DataTypeError), ('a', [1, 2, 3], 4, TypeError), ) def test_choice_sanitization(): for subtype, choices, value in GOOD_CHOICE_VALUES: yield check_good_choice_value, subtype, choices, value for subtype, choices, value, exc in BAD_CHOICE_VALUES: yield check_bad_choice_value, subtype, choices, value, exc def check_good_choice_value(subtype, choices, value): dt = soc.Choice(choices, subtype) assert dt.sanitize(value) == value def check_bad_choice_value(subtype, choices, value, exc): try: dt = soc.Choice(choices, subtype) dt.sanitize(value) except exc: pass else: assert False, 'Invalid choice allowed: %s' % value setoptconf-0.2.0/test/test_directory_modifiers.py0000644000076600000240000000120412301502243024135 0ustar jasonsimeonestaff00000000000000import setoptconf as soc def test_home_directory(): test = soc.HomeDirectory('foo.bar') result = test() # Can't really predict the output, as it's environment-dependent. # Just make sure we have something that looks like a path to our file. assert result.startswith('/') assert result.endswith('foo.bar') def test_config_directory(): test = soc.ConfigDirectory('foo.bar') result = test() # Can't really predict the output, as it's environment-dependent. # Just make sure we have something that looks like a path to our file. assert result.startswith('/') assert result.endswith('foo.bar') setoptconf-0.2.0/test/test_file_sources.py0000644000076600000240000001334012301502147022561 0ustar jasonsimeonestaff00000000000000import atexit import os.path import tempfile import setoptconf as soc _TEMPFILES = [] def make_temp(content): name = tempfile.mkstemp()[1] fp = open(name, 'w') fp.write(content) fp.close() _TEMPFILES.append(name) return name def cleanup_temps(): for temp in _TEMPFILES: if os.path.exists(temp): os.remove(temp) atexit.register(cleanup_temps) def make_settings(): settings = [] setting = soc.StringSetting('foo') settings.append(setting) setting = soc.IntegerSetting('bar') settings.append(setting) setting = soc.BooleanSetting('baz', default=False) settings.append(setting) setting = soc.ListSetting('happy', soc.String) settings.append(setting) return settings def test_directory_modifier(): file1 = make_temp(""" [mytest] foo = hello bar = 123 """) source = soc.ConfigFileSource( [file1, soc.HomeDirectory('foobar')], section='mytest' ) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None def test_configfile(): file1 = make_temp('') source = soc.ConfigFileSource(file1, section='mytest') config = source.get_config(make_settings()) assert config.foo is None assert config.bar is None assert config.baz is False assert config.happy is None file2 = make_temp(""" [mytest] foo = hello bar = 123 """) source = soc.ConfigFileSource(file2, section='mytest') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None file3 = make_temp(""" [mytest] foo = hello bar: 123 baz = true happy:foo,"bar" """) source = soc.ConfigFileSource(file3, section='mytest') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] file4 = make_temp(""" [mytest] foo = goodbye """) source = soc.ConfigFileSource([file3,file4], section='mytest') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] source = soc.ConfigFileSource([file4,file3], section='mytest', combine=True) config = source.get_config(make_settings()) assert config.foo == 'goodbye' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] try: source = soc.ConfigFileSource(123, section='mytest') except TypeError: pass else: assert False, 'Expected TypeError for bogus file name' try: source = soc.ConfigFileSource(['foobar', 123], section='mytest') except TypeError: pass else: assert False, 'Expected TypeError for bogus file name' def test_jsonfile(): file1 = make_temp('') source = soc.JsonFileSource(file1) config = source.get_config(make_settings()) assert config.foo is None assert config.bar is None assert config.baz is False assert config.happy is None file2 = make_temp(""" { "foo": "hello", "bar": 123 } """) source = soc.JsonFileSource(file2) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None file3 = make_temp(""" { "foo": "hello", "bar": 123, "baz": true, "happy": ["foo", "bar"] } """) source = soc.JsonFileSource(file3) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] file4 = make_temp('{}') source = soc.JsonFileSource(file4) config = source.get_config(make_settings()) assert config.foo is None assert config.bar is None assert config.baz is False assert config.happy is None file5 = make_temp('"foo"') source = soc.JsonFileSource(file5) try: config = source.get_config(make_settings()) except TypeError: pass else: assert False, 'Expected TypeError for non-objects' if hasattr(soc, 'YamlFileSource'): def test_yamlfile(): file1 = make_temp('') source = soc.YamlFileSource(file1) config = source.get_config(make_settings()) assert config.foo is None assert config.bar is None assert config.baz is False assert config.happy is None file2 = make_temp(""" foo: hello bar: 123 """) source = soc.YamlFileSource(file2) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None file3 = make_temp(""" foo: hello bar: 123 baz: true happy: - foo - bar """) source = soc.YamlFileSource(file3) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] file4 = make_temp('{}') source = soc.YamlFileSource(file4) config = source.get_config(make_settings()) assert config.foo is None assert config.bar is None assert config.baz is False assert config.happy is None file5 = make_temp('"foo"') source = soc.YamlFileSource(file5) try: config = source.get_config(make_settings()) except TypeError: pass else: assert False, 'Expected TypeError for non-objects' setoptconf-0.2.0/test/test_manager.py0000644000076600000240000000142412264405407021522 0ustar jasonsimeonestaff00000000000000import os import setoptconf as soc class Blah(object): foo = 'a' os.environ['TESTME_BAR'] = '123' mgr = soc.ConfigurationManager('testme') mgr.add(soc.StringSetting('foo')) mgr.add(soc.IntegerSetting('bar', required=True)) mgr.add(soc.BooleanSetting('baz', default=True)) config = mgr.retrieve( soc.EnvironmentVariableSource, soc.ObjectSource(Blah), soc.MappingSource({'baz': False}), ) assert config.foo == 'a' assert config.bar == 123 assert config.baz is False try: mgr.add('blah') except TypeError: pass else: assert False, 'Expected TypeError for bogus Setting' try: config = mgr.retrieve( soc.EnvironmentVariableSource, 'foo', ) except TypeError: pass else: assert False, 'Expected TypeError for bogus Source' setoptconf-0.2.0/test/test_settings.py0000644000076600000240000000175512313362572021757 0ustar jasonsimeonestaff00000000000000from decimal import Decimal import setoptconf as soc GOOD_NAMES = ( 'foo', 'foo_bar', 'foo123', 'foo_bar_baz', ) BAD_NAMES = ( '_foo', '1foo', 'FOO', 'foo_', 'foo__bar', 'foo-bar', ) def test_name(): for name in GOOD_NAMES: yield check_good_name, name for name in BAD_NAMES: yield check_bad_name, name def check_good_name(name): setting = soc.StringSetting(name) def check_bad_name(name): try: setting = soc.StringSetting(name) except soc.NamingError: pass else: assert False, 'Invalid name allowed: %s' % name def test_list_setting(): setting = soc.ListSetting('foo', soc.String) assert setting.name == 'foo' setting.value = ['bar', 'baz'] assert setting.value == ['bar', 'baz'] def test_choice_setting(): setting = soc.ChoiceSetting('foo', ['bar', 'baz'], soc.String) assert setting.name == 'foo' setting.value = 'baz' assert setting.value == 'baz' setoptconf-0.2.0/test/test_sources.py0000644000076600000240000001175012313362670021575 0ustar jasonsimeonestaff00000000000000import os import setoptconf as soc def make_settings(): settings = [] setting = soc.StringSetting('foo') settings.append(setting) setting = soc.IntegerSetting('bar') settings.append(setting) setting = soc.BooleanSetting('baz', default=False) settings.append(setting) setting = soc.ListSetting('happy', soc.String) settings.append(setting) setting = soc.ChoiceSetting('fuzziness', ['fuzzy', 'bare'], soc.String) settings.append(setting) return settings class MyConfig: foo = 'hello' bar = 123 class MyFullConfig(MyConfig): baz = True happy = ['foo', 'bar'] fuzziness = 'bare' def test_object_source(): source = soc.ObjectSource(MyConfig) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None assert config.fuzziness is None source = soc.ObjectSource(MyFullConfig) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] assert config.fuzziness == 'bare' def test_mapping_source(): mapping = { 'foo': 'hello', 'bar': 123, } source = soc.MappingSource(mapping) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None assert config.fuzziness is None mapping['baz'] = True mapping['happy'] = ['foo', 'bar'] mapping['fuzziness'] = 'bare' source = soc.MappingSource(mapping) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] assert config.fuzziness == 'bare' def test_environment_source(): source = soc.EnvironmentVariableSource() config = source.get_config(make_settings()) assert config.foo is None assert config.bar is None assert config.baz is False assert config.happy is None assert config.fuzziness is None os.environ['MYTEST_FOO'] = 'hello' os.environ['MYTEST_BAR'] = '123' os.environ['MYTEST_HAPPY'] = '' source = soc.EnvironmentVariableSource('mytest') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy == [] assert config.fuzziness is None os.environ['MYTEST_BAZ'] = 'yes' os.environ['MYTEST_HAPPY'] = 'foo,"bar"' os.environ['MYTEST_FUZZINESS'] = 'bare' source = soc.EnvironmentVariableSource('mytest') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] assert config.fuzziness == 'bare' def test_commandline_source(): source = soc.CommandLineSource('--foo=hello --bar=123') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None assert config.fuzziness is None source = soc.CommandLineSource('--foo=hello --bar=123 --baz --happy=foo --happy=bar --fuzziness=bare') config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] assert config.fuzziness == 'bare' source = soc.CommandLineSource(['--foo=hello','--bar=123','--baz','--happy=foo','--happy=bar','--fuzziness=bare']) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is True assert config.happy == ['foo', 'bar'] assert config.fuzziness == 'bare' try: source = soc.CommandLineSource(123) except TypeError: pass else: assert False, 'Expected TypeError for bogus arguments' options = { 'foo': { 'flags': ['--something'], }, 'bar': { 'flags': ['-z', '--zoo'], }, } source = soc.CommandLineSource('--something=hello -z=123', options=options) config = source.get_config(make_settings()) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None assert config.fuzziness is None class FakeManager(object): name = 'fake' positional = ( ('red', {}), ('blue', {'type': int}), ) source = soc.CommandLineSource('--foo=hello --bar=123 myred 4', positional=positional) mgr = FakeManager() config = source.get_config(make_settings(), manager=mgr) assert config.foo == 'hello' assert config.bar == 123 assert config.baz is False assert config.happy is None assert config.fuzziness is None assert mgr.arguments['red'] == 'myred' assert mgr.arguments['blue'] == 4