pax_global_header00006660000000000000000000000064141330260210014502gustar00rootroot0000000000000052 comment=3dafb138100525be4d60fde2406844ef22983096 python-setoptconf-0.3.0/000077500000000000000000000000001413302602100152055ustar00rootroot00000000000000python-setoptconf-0.3.0/LICENSE000066400000000000000000000020621413302602100162120ustar00rootroot00000000000000The 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. python-setoptconf-0.3.0/MANIFEST.in000066400000000000000000000000341413302602100167400ustar00rootroot00000000000000include README.rst LICENSE python-setoptconf-0.3.0/PKG-INFO000066400000000000000000000057011413302602100163050ustar00rootroot00000000000000Metadata-Version: 2.1 Name: setoptconf Version: 0.3.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://github.com/jayclassless/setoptconf/workflows/Test/badge.svg :target: https://github.com/jayclassless/setoptconf/actions ``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 :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.0 Provides-Extra: YAML python-setoptconf-0.3.0/README.rst000066400000000000000000000033731413302602100167020ustar00rootroot00000000000000========== setoptconf ========== .. image:: https://github.com/jayclassless/setoptconf/workflows/Test/badge.svg :target: https://github.com/jayclassless/setoptconf/actions ``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``. python-setoptconf-0.3.0/setoptconf.egg-info/000077500000000000000000000000001413302602100210635ustar00rootroot00000000000000python-setoptconf-0.3.0/setoptconf.egg-info/PKG-INFO000066400000000000000000000057011413302602100221630ustar00rootroot00000000000000Metadata-Version: 2.1 Name: setoptconf Version: 0.3.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://github.com/jayclassless/setoptconf/workflows/Test/badge.svg :target: https://github.com/jayclassless/setoptconf/actions ``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 :: 3 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.0 Provides-Extra: YAML python-setoptconf-0.3.0/setoptconf.egg-info/SOURCES.txt000066400000000000000000000015171413302602100227530ustar00rootroot00000000000000LICENSE 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.pypython-setoptconf-0.3.0/setoptconf.egg-info/dependency_links.txt000066400000000000000000000000011413302602100251310ustar00rootroot00000000000000 python-setoptconf-0.3.0/setoptconf.egg-info/requires.txt000066400000000000000000000000171413302602100234610ustar00rootroot00000000000000 [YAML] pyyaml python-setoptconf-0.3.0/setoptconf.egg-info/top_level.txt000066400000000000000000000000131413302602100236070ustar00rootroot00000000000000setoptconf python-setoptconf-0.3.0/setoptconf/000077500000000000000000000000001413302602100173715ustar00rootroot00000000000000python-setoptconf-0.3.0/setoptconf/__init__.py000066400000000000000000000002751413302602100215060ustar00rootroot00000000000000 # pylint: disable=W0401 from .config import * from .datatype import * from .exception import * from .manager import * from .setting import * from .source import * __version__ = '0.3.0' python-setoptconf-0.3.0/setoptconf/config.py000066400000000000000000000046151413302602100212160ustar00rootroot00000000000000from .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 'Configuration(%s)' % ( ', '.join([ '%s=%s' % (name, repr(self[name])) for name in self ]) ) def __repr__(self): # pragma: no cover return '<%s>' % str(self) python-setoptconf-0.3.0/setoptconf/datatype.py000066400000000000000000000063771413302602100215730ustar00rootroot00000000000000from .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 = str(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): return True if value else False if isinstance(value, str) 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 python-setoptconf-0.3.0/setoptconf/exception.py000066400000000000000000000005651413302602100217470ustar00rootroot00000000000000 __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 python-setoptconf-0.3.0/setoptconf/manager.py000066400000000000000000000022111413302602100213510ustar00rootroot00000000000000from 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 python-setoptconf-0.3.0/setoptconf/setting.py000066400000000000000000000033331413302602100214220ustar00rootroot00000000000000 # 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 str(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) python-setoptconf-0.3.0/setoptconf/source/000077500000000000000000000000001413302602100206715ustar00rootroot00000000000000python-setoptconf-0.3.0/setoptconf/source/__init__.py000066400000000000000000000005221413302602100230010ustar00rootroot00000000000000 # 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 * python-setoptconf-0.3.0/setoptconf/source/base.py000066400000000000000000000002301413302602100221500ustar00rootroot00000000000000 __all__ = ( 'Source', ) class Source(object): def get_config(self, settings, manager=None, parent=None): raise NotImplementedError() python-setoptconf-0.3.0/setoptconf/source/commandline.py000066400000000000000000000120451413302602100235330ustar00rootroot00000000000000import 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, str): 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) python-setoptconf-0.3.0/setoptconf/source/configfile.py000066400000000000000000000017231413302602100233530ustar00rootroot00000000000000import 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 python-setoptconf-0.3.0/setoptconf/source/environment.py000066400000000000000000000017571413302602100236210ustar00rootroot00000000000000import 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] python-setoptconf-0.3.0/setoptconf/source/filebased.py000066400000000000000000000055311413302602100231650ustar00rootroot00000000000000import 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, (str, 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, str): 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() python-setoptconf-0.3.0/setoptconf/source/jsonfile.py000066400000000000000000000015251413302602100230570ustar00rootroot00000000000000import 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 python-setoptconf-0.3.0/setoptconf/source/mapping.py000066400000000000000000000007621413302602100227030ustar00rootroot00000000000000from ..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) python-setoptconf-0.3.0/setoptconf/source/modobj.py000066400000000000000000000033531413302602100225210ustar00rootroot00000000000000import 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, str): 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, str): 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) python-setoptconf-0.3.0/setoptconf/source/yamlfile.py000066400000000000000000000015331413302602100230470ustar00rootroot00000000000000import 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 python-setoptconf-0.3.0/setoptconf/util.py000066400000000000000000000010331413302602100207150ustar00rootroot00000000000000import csv import io import sys __all__ = ( 'csv_to_list', 'UnicodeMixin', ) def csv_to_list(value): if isinstance(value, str) and value: reader = csv.reader(io.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: str(x).encode('utf-8') python-setoptconf-0.3.0/setup.cfg000066400000000000000000000004271413302602100170310ustar00rootroot00000000000000[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 python-setoptconf-0.3.0/setup.py000066400000000000000000000027221413302602100167220ustar00rootroot00000000000000import os.path import re import sys from setuptools import setup, find_packages DEPENDENCIES = [] 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 :: 3', 'Programming Language :: Python :: 3 :: Only', 'Topic :: Software Development :: Libraries :: Python Modules' ], packages=find_packages(exclude=['test.*', 'test']), install_requires=DEPENDENCIES, extras_require={ 'YAML': ['pyyaml'], }, python_requires=">=3.0", ) python-setoptconf-0.3.0/test/000077500000000000000000000000001413302602100161645ustar00rootroot00000000000000python-setoptconf-0.3.0/test/test_configuration.py000066400000000000000000000076701413302602100224560ustar00rootroot00000000000000 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' python-setoptconf-0.3.0/test/test_datatypes.py000066400000000000000000000105241413302602100215750ustar00rootroot00000000000000from 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 python-setoptconf-0.3.0/test/test_directory_modifiers.py000066400000000000000000000012041413302602100236370ustar00rootroot00000000000000import 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') python-setoptconf-0.3.0/test/test_file_sources.py000066400000000000000000000133401413302602100222600ustar00rootroot00000000000000import 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' python-setoptconf-0.3.0/test/test_manager.py000066400000000000000000000014241413302602100212100ustar00rootroot00000000000000import 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' python-setoptconf-0.3.0/test/test_settings.py000066400000000000000000000017551413302602100214450ustar00rootroot00000000000000from 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' python-setoptconf-0.3.0/test/test_sources.py000066400000000000000000000117501413302602100212640ustar00rootroot00000000000000import 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