[Python-modules-commits] [python-setoptconf] 01/02: Imported Upstream version 0.2.0

Daniel Stender danstender-guest at moszumanska.debian.org
Sun Jun 21 18:08:36 UTC 2015


This is an automated email from the git hooks/post-receive script.

danstender-guest pushed a commit to branch master
in repository python-setoptconf.

commit 5ac9ccba2cbe8d8a5374eead07e2d3e4aa479a45
Author: Daniel Stender <debian at danielstender.com>
Date:   Sun Jun 21 20:04:51 2015 +0200

    Imported Upstream version 0.2.0
---
 LICENSE                                  |  22 +++
 MANIFEST.in                              |   2 +
 PKG-INFO                                 |  79 ++++++++++
 README.rst                               |  57 +++++++
 setoptconf.egg-info/PKG-INFO             |  79 ++++++++++
 setoptconf.egg-info/SOURCES.txt          |  34 ++++
 setoptconf.egg-info/dependency_links.txt |   1 +
 setoptconf.egg-info/requires.txt         |   4 +
 setoptconf.egg-info/top_level.txt        |   1 +
 setoptconf/__init__.py                   |  12 ++
 setoptconf/config.py                     |  84 ++++++++++
 setoptconf/datatype.py                   | 135 ++++++++++++++++
 setoptconf/exception.py                  |  28 ++++
 setoptconf/manager.py                    |  44 ++++++
 setoptconf/setting.py                    |  81 ++++++++++
 setoptconf/source/__init__.py            |  19 +++
 setoptconf/source/base.py                |   9 ++
 setoptconf/source/commandline.py         | 167 ++++++++++++++++++++
 setoptconf/source/configfile.py          |  35 +++++
 setoptconf/source/environment.py         |  38 +++++
 setoptconf/source/filebased.py           | 105 +++++++++++++
 setoptconf/source/jsonfile.py            |  33 ++++
 setoptconf/source/mapping.py             |  20 +++
 setoptconf/source/modobj.py              |  59 +++++++
 setoptconf/source/yamlfile.py            |  34 ++++
 setoptconf/util.py                       |  26 ++++
 setup.cfg                                |  21 +++
 setup.py                                 |  70 +++++++++
 test/test_configuration.py               | 179 +++++++++++++++++++++
 test/test_datatypes.py                   | 158 +++++++++++++++++++
 test/test_directory_modifiers.py         |  22 +++
 test/test_file_sources.py                | 260 +++++++++++++++++++++++++++++++
 test/test_manager.py                     |  44 ++++++
 test/test_settings.py                    |  58 +++++++
 test/test_sources.py                     | 182 ++++++++++++++++++++++
 35 files changed, 2202 insertions(+)

diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..37c3132
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+The 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.
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..3b8be7d
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include README.rst LICENSE
+
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..5a43079
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,79 @@
+Metadata-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 at 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
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..0f6e401
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,57 @@
+==========
+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``.
+
diff --git a/setoptconf.egg-info/PKG-INFO b/setoptconf.egg-info/PKG-INFO
new file mode 100644
index 0000000..5a43079
--- /dev/null
+++ b/setoptconf.egg-info/PKG-INFO
@@ -0,0 +1,79 @@
+Metadata-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 at 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
diff --git a/setoptconf.egg-info/SOURCES.txt b/setoptconf.egg-info/SOURCES.txt
new file mode 100644
index 0000000..7315f41
--- /dev/null
+++ b/setoptconf.egg-info/SOURCES.txt
@@ -0,0 +1,34 @@
+LICENSE
+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.py
\ No newline at end of file
diff --git a/setoptconf.egg-info/dependency_links.txt b/setoptconf.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/setoptconf.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/setoptconf.egg-info/requires.txt b/setoptconf.egg-info/requires.txt
new file mode 100644
index 0000000..bc55498
--- /dev/null
+++ b/setoptconf.egg-info/requires.txt
@@ -0,0 +1,4 @@
+
+
+[YAML]
+pyyaml
\ No newline at end of file
diff --git a/setoptconf.egg-info/top_level.txt b/setoptconf.egg-info/top_level.txt
new file mode 100644
index 0000000..10ce273
--- /dev/null
+++ b/setoptconf.egg-info/top_level.txt
@@ -0,0 +1 @@
+setoptconf
diff --git a/setoptconf/__init__.py b/setoptconf/__init__.py
new file mode 100644
index 0000000..f136a82
--- /dev/null
+++ b/setoptconf/__init__.py
@@ -0,0 +1,12 @@
+
+# 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'
diff --git a/setoptconf/config.py b/setoptconf/config.py
new file mode 100644
index 0000000..1906cf6
--- /dev/null
+++ b/setoptconf/config.py
@@ -0,0 +1,84 @@
+from .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)
diff --git a/setoptconf/datatype.py b/setoptconf/datatype.py
new file mode 100644
index 0000000..f89baff
--- /dev/null
+++ b/setoptconf/datatype.py
@@ -0,0 +1,135 @@
+from .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
diff --git a/setoptconf/exception.py b/setoptconf/exception.py
new file mode 100644
index 0000000..2f02654
--- /dev/null
+++ b/setoptconf/exception.py
@@ -0,0 +1,28 @@
+
+__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
diff --git a/setoptconf/manager.py b/setoptconf/manager.py
new file mode 100644
index 0000000..1b5b791
--- /dev/null
+++ b/setoptconf/manager.py
@@ -0,0 +1,44 @@
+from 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
diff --git a/setoptconf/setting.py b/setoptconf/setting.py
new file mode 100644
index 0000000..f8e008b
--- /dev/null
+++ b/setoptconf/setting.py
@@ -0,0 +1,81 @@
+
+# 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)
diff --git a/setoptconf/source/__init__.py b/setoptconf/source/__init__.py
new file mode 100644
index 0000000..8eea290
--- /dev/null
+++ b/setoptconf/source/__init__.py
@@ -0,0 +1,19 @@
+
+# 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 *
diff --git a/setoptconf/source/base.py b/setoptconf/source/base.py
new file mode 100644
index 0000000..b41e051
--- /dev/null
+++ b/setoptconf/source/base.py
@@ -0,0 +1,9 @@
+
+__all__ = (
+    'Source',
+)
+
+
+class Source(object):
+    def get_config(self, settings, manager=None, parent=None):
+        raise NotImplementedError()
diff --git a/setoptconf/source/commandline.py b/setoptconf/source/commandline.py
new file mode 100644
index 0000000..9b6394b
--- /dev/null
+++ b/setoptconf/source/commandline.py
@@ -0,0 +1,167 @@
+import 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:
... 1457 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-setoptconf.git



More information about the Python-modules-commits mailing list