Hello community,

here is the log from the commit of package python-oslo.config for 
openSUSE:Factory checked in at 2015-12-21 12:04:18
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-oslo.config (Old)
 and      /work/SRC/openSUSE:Factory/.python-oslo.config.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-oslo.config"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-oslo.config/python-oslo.config.changes    
2015-09-02 07:53:57.000000000 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-oslo.config.new/python-oslo.config.changes   
    2015-12-21 12:04:19.000000000 +0100
@@ -1,0 +2,26 @@
+Fri Nov 20 07:03:21 UTC 2015 - [email protected]
+
+- update to 2.7.0:
+  * Add max length check to StrOpt
+  * Fix StrOpt to allow setting quotes and regex
+  * Improve default type checking
+  * Use versionadded and versionchanged in doc
+  * update doc to use new type PortOpt
+  * Fix versionchanged
+  * Raise exception when wrong type is used as default
+  * Add PortOpt for integer with range 1 to 65535
+  * Fix typos in cfg.py
+  * Case insensitiveness when comparing choices values
+  * Add missing tests and generator code for IPOpt
+  * Fix coverage configuration and execution
+  * Add item_type and bounds to ListOpt
+  * Fix bad exception for StrOpt with invalid choice value
+  * fix literal rendering in dictionary options docs
+  * Fix home-page value in setup.cfg with Oslo wiki
+  * docs - Set pbr 'warnerrors' option for doc build
+  * Include changelog/history in docs
+  * Correct the comment of __call__ method
+  * Demonstrate that MultiStr values from multiple sources are merged
+  * Make Integer type class honor zero for min/max
+
+-------------------------------------------------------------------

Old:
----
  oslo.config-2.3.0.tar.gz

New:
----
  oslo.config-2.7.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-oslo.config.spec ++++++
--- /var/tmp/diff_new_pack.MG2X3C/_old  2015-12-21 12:04:20.000000000 +0100
+++ /var/tmp/diff_new_pack.MG2X3C/_new  2015-12-21 12:04:20.000000000 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           python-oslo.config
-Version:        2.3.0
+Version:        2.7.0
 Release:        0
 Summary:        OpenStack configuration API
 License:        Apache-2.0

++++++ oslo.config-2.3.0.tar.gz -> oslo.config-2.7.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/.coveragerc 
new/oslo.config-2.7.0/.coveragerc
--- old/oslo.config-2.3.0/.coveragerc   1970-01-01 01:00:00.000000000 +0100
+++ new/oslo.config-2.7.0/.coveragerc   2015-11-16 21:22:43.000000000 +0100
@@ -0,0 +1,8 @@
+[run]
+branch = True
+source = oslo_config
+omit = oslo_config/tests/*
+
+[report]
+ignore_errors = True
+precision = 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/AUTHORS 
new/oslo.config-2.7.0/AUTHORS
--- old/oslo.config-2.3.0/AUTHORS       2015-08-24 23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/AUTHORS       2015-11-16 21:23:02.000000000 +0100
@@ -1,6 +1,8 @@
+Akihiro Motoki <[email protected]>
 Alex Gaynor <[email protected]>
 Andreas Jaeger <[email protected]>
 Anthony Young <[email protected]>
+BaoLiang Cui <[email protected]>
 Ben Nemec <[email protected]>
 Brant Knudson <[email protected]>
 Brian Waldon <[email protected]>
@@ -10,7 +12,9 @@
 Chris St. Pierre <[email protected]>
 Christian Berendt <[email protected]>
 Chuck Short <[email protected]>
+Chung Chih, Hung <[email protected]>
 Cyril Roelandt <[email protected]>
+Daisuke Fujita <[email protected]>
 Dan Prince <[email protected]>
 Davanum Srinivas <[email protected]>
 Davanum Srinivas <[email protected]>
@@ -41,6 +45,7 @@
 Joe Gordon <[email protected]>
 Joe Heck <[email protected]>
 Johannes Erdfelt <[email protected]>
+John L. Villalovos <[email protected]>
 Jonathan LaCour <[email protected]>
 Joshua Harlow <[email protected]>
 Joshua Harlow <[email protected]>
@@ -50,10 +55,12 @@
 Laurence Miao <[email protected]>
 Lianhao Lu <[email protected]>
 LingxianKong <[email protected]>
+Lucas Alvares Gomes <[email protected]>
 Luis A. Garcia <[email protected]>
 Mark McLoughlin <[email protected]>
 Mark Vanderwiel <[email protected]>
 Markus Zoeller <[email protected]>
+Masaki Matsushita <[email protected]>
 Matt Riedemann <[email protected]>
 Matthew Treinish <[email protected]>
 Maxim Kulkin <[email protected]>
@@ -69,6 +76,7 @@
 Petr Blaho <[email protected]>
 Radomir Dopieralski <[email protected]>
 Rick Harris <[email protected]>
+Ronald Bradford <[email protected]>
 Roxana Gherle <[email protected]>
 Rushi Agrawal <[email protected]>
 Russell Bryant <[email protected]>
@@ -82,10 +90,12 @@
 Tim Miller <[email protected]>
 Tom Cammann <[email protected]>
 Tong Damon Da <[email protected]>
+Victor Stinner <[email protected]>
 Vincent Untz <[email protected]>
 Vishvananda Ishaya <[email protected]>
 YAMAMOTO Takashi <[email protected]>
 Yuriy Taraday <[email protected]>
+ZhiQiang Fan <[email protected]>
 Zhongyue Luo <[email protected]>
 Zhongyue Luo <[email protected]>
 liu-sheng <[email protected]>
@@ -93,4 +103,5 @@
 llg8212 <[email protected]>
 lzyeval <[email protected]>
 skudriashev <[email protected]>
+venkatamahesh <[email protected]>
 vponomaryov <[email protected]>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/ChangeLog 
new/oslo.config-2.7.0/ChangeLog
--- old/oslo.config-2.3.0/ChangeLog     2015-08-24 23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/ChangeLog     2015-11-16 21:23:02.000000000 +0100
@@ -1,6 +1,47 @@
 CHANGES
 =======
 
+2.7.0
+-----
+
+* Add max length check to StrOpt
+* Remove "Kept for backward-compatibility" comment
+* Fix StrOpt to allow setting quotes and regex
+* Improve default type checking
+* Use versionadded and versionchanged in doc
+* update doc to use new type PortOpt
+* Fix versionchanged
+* Raise exception when wrong type is used as default
+
+2.6.0
+-----
+
+* Add PortOpt for integer with range 1 to 65535
+
+2.5.0
+-----
+
+* Fix typos in cfg.py
+* Case insensitiveness when comparing choices values
+* Add missing tests and generator code for IPOpt
+* Fix coverage configuration and execution
+* Add item_type and bounds to ListOpt
+* Fix bad exception for StrOpt with invalid choice value
+* fix literal rendering in dictionary options docs
+* Fix home-page value in setup.cfg with Oslo wiki
+* docs - Set pbr 'warnerrors' option for doc build
+* Include changelog/history in docs
+* Document DictOpt class
+* Correct the comment of __call__ method
+* Updated from global requirements
+* Demonstrate that MultiStr values from multiple sources are merged
+
+2.4.0
+-----
+
+* Updated from global requirements
+* Make Integer type class honor zero for min/max
+
 2.3.0
 -----
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/PKG-INFO 
new/oslo.config-2.7.0/PKG-INFO
--- old/oslo.config-2.3.0/PKG-INFO      2015-08-24 23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/PKG-INFO      2015-11-16 21:23:02.000000000 +0100
@@ -1,8 +1,8 @@
 Metadata-Version: 1.1
 Name: oslo.config
-Version: 2.3.0
+Version: 2.7.0
 Summary: Oslo Configuration API
-Home-page: https://launchpad.net/oslo
+Home-page: https://wiki.openstack.org/wiki/Oslo#oslo.config
 Author: OpenStack
 Author-email: [email protected]
 License: UNKNOWN
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/doc/source/generator.rst 
new/oslo.config-2.7.0/doc/source/generator.rst
--- old/oslo.config-2.3.0/doc/source/generator.rst      2015-08-24 
23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/doc/source/generator.rst      2015-11-16 
21:22:43.000000000 +0100
@@ -16,6 +16,8 @@
 
   $> oslo-config-generator --namespace myapp --namespace oslo.messaging > 
myapp.conf
 
+.. versionadded:: 1.4
+
 Defining Option Discovery Entry Points
 --------------------------------------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/doc/source/history.rst 
new/oslo.config-2.7.0/doc/source/history.rst
--- old/oslo.config-2.3.0/doc/source/history.rst        1970-01-01 
01:00:00.000000000 +0100
+++ new/oslo.config-2.7.0/doc/source/history.rst        2015-11-16 
21:22:43.000000000 +0100
@@ -0,0 +1,2 @@
+.. include:: ../../ChangeLog
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/doc/source/index.rst 
new/oslo.config-2.7.0/doc/source/index.rst
--- old/oslo.config-2.3.0/doc/source/index.rst  2015-08-24 23:32:24.000000000 
+0200
+++ new/oslo.config-2.7.0/doc/source/index.rst  2015-11-16 21:22:43.000000000 
+0100
@@ -26,6 +26,14 @@
    contributing
    sphinxconfiggen
 
+Release Notes
+=============
+
+.. toctree::
+   :maxdepth: 1
+
+   history
+
 Indices and tables
 ==================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/doc/source/opts.rst 
new/oslo.config-2.7.0/doc/source/opts.rst
--- old/oslo.config-2.3.0/doc/source/opts.rst   2015-08-24 23:32:24.000000000 
+0200
+++ new/oslo.config-2.7.0/doc/source/opts.rst   2015-11-16 21:22:43.000000000 
+0100
@@ -14,6 +14,7 @@
 .. autoclass:: MultiOpt
 .. autoclass:: MultiStrOpt
 .. autoclass:: IPOpt
+.. autoclass:: PortOpt
 .. autoclass:: DeprecatedOpt
 .. autoclass:: SubCommandOpt
 .. autoclass:: OptGroup
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/doc/source/styleguide.rst 
new/oslo.config-2.7.0/doc/source/styleguide.rst
--- old/oslo.config-2.3.0/doc/source/styleguide.rst     2015-08-24 
23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/doc/source/styleguide.rst     2015-11-16 
21:22:43.000000000 +0100
@@ -16,9 +16,9 @@
     cfg.StrOpt('bind_host',
                default='0.0.0.0',
                help='IP address to listen on.'),
-    cfg.IntOpt('bind_port',
-               default=9292,
-               help='Port number to listen on.')
+    cfg.PortOpt('bind_port',
+                default=9292,
+                help='Port number to listen on.')
 
 
 Style Guide
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo.config.egg-info/PKG-INFO 
new/oslo.config-2.7.0/oslo.config.egg-info/PKG-INFO
--- old/oslo.config-2.3.0/oslo.config.egg-info/PKG-INFO 2015-08-24 
23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/oslo.config.egg-info/PKG-INFO 2015-11-16 
21:23:02.000000000 +0100
@@ -1,8 +1,8 @@
 Metadata-Version: 1.1
 Name: oslo.config
-Version: 2.3.0
+Version: 2.7.0
 Summary: Oslo Configuration API
-Home-page: https://launchpad.net/oslo
+Home-page: https://wiki.openstack.org/wiki/Oslo#oslo.config
 Author: OpenStack
 Author-email: [email protected]
 License: UNKNOWN
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo.config.egg-info/SOURCES.txt 
new/oslo.config-2.7.0/oslo.config.egg-info/SOURCES.txt
--- old/oslo.config-2.3.0/oslo.config.egg-info/SOURCES.txt      2015-08-24 
23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/oslo.config.egg-info/SOURCES.txt      2015-11-16 
21:23:02.000000000 +0100
@@ -1,3 +1,4 @@
+.coveragerc
 .testr.conf
 AUTHORS
 CONTRIBUTING.rst
@@ -21,6 +22,7 @@
 doc/source/fixture.rst
 doc/source/generator.rst
 doc/source/helpers.rst
+doc/source/history.rst
 doc/source/index.rst
 doc/source/namespaces.rst
 doc/source/opts.rst
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo.config.egg-info/pbr.json 
new/oslo.config-2.7.0/oslo.config.egg-info/pbr.json
--- old/oslo.config-2.3.0/oslo.config.egg-info/pbr.json 2015-08-24 
23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/oslo.config.egg-info/pbr.json 2015-11-16 
21:23:02.000000000 +0100
@@ -1 +1 @@
-{"is_release": true, "git_version": "bc13758"}
\ No newline at end of file
+{"git_version": "260891b", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo.config.egg-info/requires.txt 
new/oslo.config-2.7.0/oslo.config.egg-info/requires.txt
--- old/oslo.config-2.3.0/oslo.config.egg-info/requires.txt     2015-08-24 
23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/oslo.config.egg-info/requires.txt     2015-11-16 
21:23:02.000000000 +0100
@@ -1,4 +1,4 @@
 argparse
-netaddr>=0.7.12
+netaddr!=0.7.16,>=0.7.12
 six>=1.9.0
-stevedore>=1.5.0 # Apache-2.0
+stevedore>=1.5.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo_config/cfg.py 
new/oslo.config-2.7.0/oslo_config/cfg.py
--- old/oslo.config-2.3.0/oslo_config/cfg.py    2015-08-24 23:32:24.000000000 
+0200
+++ new/oslo.config-2.7.0/oslo_config/cfg.py    2015-11-16 21:22:43.000000000 
+0100
@@ -166,9 +166,9 @@
     rabbit_host_opt = cfg.StrOpt('host',
                                  default='localhost',
                                  help='IP/hostname to listen on.'),
-    rabbit_port_opt = cfg.IntOpt('port',
-                                 default=5672,
-                                 help='Port number to listen on.')
+    rabbit_port_opt = cfg.PortOpt('port',
+                                  default=5672,
+                                  help='Port number to listen on.')
 
     def register_rabbit_opts(conf):
         conf.register_group(rabbit_group)
@@ -266,6 +266,26 @@
         ...
      ]
 
+Dictionary Options
+------------------
+
+If you need end users to specify a dictionary of key/value pairs, then you can
+use the DictOpt::
+
+    opts = [
+        cfg.DictOpt('foo',
+                    default={})
+    ]
+
+The end users can then specify the option foo in their configuration file
+as shown below:
+
+.. code-block:: ini
+
+    [DEFAULT]
+    foo = k1:v1,k2:v2
+
+
 Global ConfigOpts
 -----------------
 
@@ -276,7 +296,7 @@
 
     opts = [
         cfg.StrOpt('bind_host', default='0.0.0.0'),
-        cfg.IntOpt('bind_port', default=9292),
+        cfg.PortOpt('bind_port', default=9292),
     ]
 
     CONF = cfg.CONF
@@ -465,11 +485,15 @@
         return 'Failed to parse %s: %s' % (self.config_file, self.msg)
 
 
-class ConfigFileValueError(Error):
+class ConfigFileValueError(Error, ValueError):
     """Raised if a config file value does not match its opt type."""
     pass
 
 
+class DefaultValueError(Error, ValueError):
+    """Raised if a default config type does not fit the opt type."""
+
+
 def _fixpath(p):
     """Apply tilde expansion and absolutization to a path."""
     return os.path.abspath(os.path.expanduser(p))
@@ -653,6 +677,19 @@
     .. py:attribute:: help
 
         a string explaining how the option's value is used
+
+    .. versionchanged:: 1.2
+       Added *deprecated_opts* parameter.
+
+    .. versionchanged:: 1.4
+       Added *sample_default* parameter.
+
+    .. versionchanged:: 1.9
+       Added *deprecated_for_removal* parameter.
+
+    .. versionchanged:: 2.7
+
+       An exception is now raised if the default value has the wrong type.
     """
     multi = False
 
@@ -694,7 +731,7 @@
         if deprecated_name is not None or deprecated_group is not None:
             self.deprecated_opts.append(DeprecatedOpt(deprecated_name,
                                                       group=deprecated_group))
-        self._assert_default_is_of_opt_type()
+        self._check_default()
 
     def _default_is_ref(self):
         """Check if default is a reference to another var."""
@@ -703,19 +740,16 @@
             return '$' in tmpl
         return False
 
-    def _assert_default_is_of_opt_type(self):
+    def _check_default(self):
         if (self.default is not None
-                and not self._default_is_ref()
-                and hasattr(self.type, 'is_base_type')
-                and not self.type.is_base_type(self.default)):
-            # NOTE(tcammann) Change this to raise error after K relase
-            expected_types = ", ".join(
-                [t.__name__ for t in self.type.BASE_TYPES])
-            LOG.debug(('Expected default value of type(s) %(extypes)s but got '
-                       '%(default)r of type %(deftypes)s'),
-                      {'extypes': expected_types,
-                       'default': self.default,
-                       'deftypes': type(self.default).__name__})
+                and not self._default_is_ref()):
+            try:
+                self.type(self.default)
+            except Exception:
+                raise DefaultValueError("Error processing default value "
+                                        "%(default)s for Opt type of %(opt)s."
+                                        % {'default': self.default,
+                                           'opt': self.type})
 
     def __ne__(self, another):
         return vars(self) != vars(another)
@@ -929,6 +963,8 @@
 
     Then the value of "[group1]/opt1" will be ['val11', 'val12', 'val21',
     'val22'].
+
+    .. versionadded:: 1.2
     """
 
     def __init__(self, name, group=None):
@@ -955,14 +991,39 @@
 
     Option with ``type`` :class:`oslo_config.types.String`
 
-    `Kept for backward-compatibility with options not using Opt directly`.
-
     :param choices: Optional sequence of valid values.
+    :param quotes: If True and string is enclosed with single or double
+                   quotes, will strip those quotes.
+    :param regex: Optional regular expression (string or compiled
+                  regex) that the value must match on an unanchored
+                  search.
+    :param ignore_case: If True case differences (uppercase vs. lowercase)
+                        between 'choices' or 'regex' will be ignored.
+    :param max_length: If positive integer, the value must be less than or
+                       equal to this parameter.
+
+    .. versionchanged:: 2.7
+       Added *quotes* parameter
+
+    .. versionchanged:: 2.7
+       Added *regex* parameter
+
+    .. versionchanged:: 2.7
+       Added *ignore_case* parameter
+
+    .. versionchanged:: 2.7
+       Added *max_length* parameter
     """
 
-    def __init__(self, name, choices=None, **kwargs):
+    def __init__(self, name, choices=None, quotes=None,
+                 regex=None, ignore_case=None, max_length=None, **kwargs):
         super(StrOpt, self).__init__(name,
-                                     type=types.String(choices=choices),
+                                     type=types.String(
+                                         choices=choices,
+                                         quotes=quotes,
+                                         regex=regex,
+                                         ignore_case=ignore_case,
+                                         max_length=max_length),
                                      **kwargs)
 
 
@@ -971,7 +1032,7 @@
     """Boolean options.
 
     Bool opts are set to True or False on the command line using --optname or
-    --noopttname respectively.
+    --nooptname respectively.
 
     In config files, boolean values are cast with Boolean type.
     """
@@ -1026,7 +1087,9 @@
 
     Option with ``type`` :class:`oslo_config.types.Integer`
 
-    `Kept for backward-compatibility with options not using Opt directly`.
+    .. versionchanged:: 1.15
+
+       Added *min* and *max* parameters.
     """
 
     def __init__(self, name, min=None, max=None, **kwargs):
@@ -1039,8 +1102,6 @@
     """Option with Float type
 
     Option with ``type`` :class:`oslo_config.types.Float`
-
-    `Kept for backward-communicability with options not using Opt directly`.
     """
 
     def __init__(self, name, **kwargs):
@@ -1053,11 +1114,15 @@
 
     Option with ``type`` :class:`oslo_config.types.List`
 
-    `Kept for backward-compatibility with options not using Opt directly`.
+    .. versionchanged:: 2.5
+       Added *item_type* and *bounds* parameters.
     """
 
-    def __init__(self, name, **kwargs):
-        super(ListOpt, self).__init__(name, type=types.List(), **kwargs)
+    def __init__(self, name, item_type=None, bounds=None, **kwargs):
+        super(ListOpt, self).__init__(name,
+                                      type=types.List(item_type=item_type,
+                                                      bounds=bounds),
+                                      **kwargs)
 
 
 class DictOpt(Opt):
@@ -1066,7 +1131,7 @@
 
     Option with ``type`` :class:`oslo_config.types.Dict`
 
-    `Kept for backward-compatibility with options not using Opt directly`.
+    .. versionadded:: 1.2
     """
 
     def __init__(self, name, **kwargs):
@@ -1081,6 +1146,8 @@
 
     :param version: one of either ``4``, ``6``, or ``None`` to specify
        either version.
+
+    .. versionadded:: 1.4
     """
 
     def __init__(self, name, version=None, **kwargs):
@@ -1088,6 +1155,19 @@
                                     **kwargs)
 
 
+class PortOpt(IntOpt):
+
+    """Option for a TCP/IP port number.  Ports can range from 1 to 65535.
+
+    Option with ``type`` :class:`oslo_config.types.Integer`
+
+    .. versionadded:: 2.6
+    """
+
+    def __init__(self, name, **kwargs):
+        super(PortOpt, self).__init__(name, min=1, max=65535, **kwargs)
+
+
 class MultiOpt(Opt):
 
     """Multi-value option.
@@ -1107,6 +1187,8 @@
 
     The command line ``--foo=1 --foo=2`` would result in ``cfg.CONF.foo``
     containing ``[1,2]``
+
+    .. versionadded:: 1.3
     """
     multi = True
 
@@ -1129,10 +1211,6 @@
 
     MultiOpt with a default :class:`oslo_config.types.MultiString` item
     type.
-
-    `Kept for backwards-compatibility for options that do not use
-    MultiOpt directly`.
-
     """
 
     def __init__(self, name, **kwargs):
@@ -1209,6 +1287,8 @@
     This allows us to properly handle the precedence of --config-file
     options over previous command line arguments, but not over subsequent
     arguments.
+
+    .. versionadded:: 1.2
     """
 
     class ConfigFileAction(argparse.Action):
@@ -1255,6 +1335,8 @@
     _Namespace object. This allows us to properly handle the precedence of
     --config-dir options over previous command line arguments, but not
     over subsequent arguments.
+
+    .. versionadded:: 1.2
     """
 
     class ConfigDirAction(argparse.Action):
@@ -1860,7 +1942,6 @@
         :param usage: a usage string (%prog will be expanded)
         :param default_config_files: config files to use by default
         :param validate_default_values: whether to validate the default values
-        :returns: the list of arguments left over after parsing options
         :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
                  ConfigFilesPermissionDeniedError,
                  RequiredOptError, DuplicateOptError
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo_config/cfgfilter.py 
new/oslo.config-2.7.0/oslo_config/cfgfilter.py
--- old/oslo.config-2.3.0/oslo_config/cfgfilter.py      2015-08-24 
23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/oslo_config/cfgfilter.py      2015-11-16 
21:22:43.000000000 +0100
@@ -24,6 +24,8 @@
 
 3. Limit the options on a Cfg object that can be accessed.
 
+.. versionadded:: 1.4
+
 Cross-Module Option Dependencies
 --------------------------------
 
@@ -121,8 +123,6 @@
 
   print(restricted_conf.foo)
   print(restricted_conf.bar)  # raises NoSuchOptError
-
-
 """
 
 import collections
@@ -132,7 +132,10 @@
 
 
 class CliOptRegisteredError(cfg.Error):
-    """Raised when registering cli opt not in original ConfigOpts."""
+    """Raised when registering cli opt not in original ConfigOpts.
+
+    .. versionadded:: 1.12
+    """
 
     def __str__(self):
         ret = "Cannot register a cli option that was not present in the" \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo_config/generator.py 
new/oslo.config-2.7.0/oslo_config/generator.py
--- old/oslo.config-2.3.0/oslo_config/generator.py      2015-08-24 
23:32:25.000000000 +0200
+++ new/oslo.config-2.7.0/oslo_config/generator.py      2015-11-16 
21:22:43.000000000 +0100
@@ -20,6 +20,7 @@
 Tool for generating a sample configuration file. See
 ../doc/source/generator.rst for details.
 
+.. versionadded:: 1.4
 """
 
 import logging
@@ -73,7 +74,8 @@
             default_str = str(opt.sample_default)
         elif opt.default is None:
             default_str = '<None>'
-        elif isinstance(opt, cfg.StrOpt):
+        elif (isinstance(opt, cfg.StrOpt) or
+              isinstance(opt, cfg.IPOpt)):
             default_str = opt.default
         elif isinstance(opt, cfg.BoolOpt):
             default_str = str(opt.default).lower()
@@ -110,6 +112,8 @@
         cfg.FloatOpt: 'floating point value',
         cfg.ListOpt: 'list value',
         cfg.DictOpt: 'dict value',
+        cfg.IPOpt: 'ip address value',
+        cfg.PortOpt: 'port value',
         cfg.MultiStrOpt: 'multi valued',
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo_config/tests/test_cfg.py 
new/oslo.config-2.7.0/oslo_config/tests/test_cfg.py
--- old/oslo.config-2.3.0/oslo_config/tests/test_cfg.py 2015-08-24 
23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/oslo_config/tests/test_cfg.py 2015-11-16 
21:22:43.000000000 +0100
@@ -476,6 +476,24 @@
          dict(opt_class=cfg.DictOpt, default=None,
               cli_args=['--old-oof', 'key1:blaa', '--old-oof', 'key2:bar'],
               value={'key2': 'bar'}, deps=('oof', 'old'))),
+        ('port_default',
+         dict(opt_class=cfg.PortOpt, default=80,
+              cli_args=[], value=80, deps=(None, None))),
+        ('port_arg',
+         dict(opt_class=cfg.PortOpt, default=None,
+              cli_args=['--foo=80'], value=80, deps=(None, None))),
+        ('port_arg_deprecated_name',
+         dict(opt_class=cfg.PortOpt, default=None,
+              cli_args=['--oldfoo=80'], value=80, deps=('oldfoo', None))),
+        ('port_arg_deprecated_group',
+         dict(opt_class=cfg.PortOpt, default=None,
+              cli_args=['--old-foo=80'], value=80, deps=(None, 'old'))),
+        ('port_arg_deprecated_group_default',
+         dict(opt_class=cfg.PortOpt, default=None,
+              cli_args=['--foo=80'], value=80, deps=(None, 'DEFAULT'))),
+        ('port_arg_deprecated_group_and_name',
+         dict(opt_class=cfg.PortOpt, default=None,
+              cli_args=['--old-oof=80'], value=80, deps=('oof', 'old'))),
         ('multistr_default',
          dict(opt_class=cfg.MultiStrOpt, default=['bar'], cli_args=[],
               value=['bar'], deps=(None, None))),
@@ -623,6 +641,24 @@
                           ['key1:blaa,key2:bar'],
                           {'key1': 'blaa', 'key2': 'bar'})
 
+    def test_positional_ip_none_default(self):
+        self._do_pos_test(cfg.IPOpt, None, [], None)
+
+    def test_positional_ip_default(self):
+        self._do_pos_test(cfg.IPOpt, '127.0.0.1', [], '127.0.0.1')
+
+    def test_positional_ip_arg(self):
+        self._do_pos_test(cfg.IPOpt, None, ['127.0.0.1'], '127.0.0.1')
+
+    def test_positional_port_none_default(self):
+        self._do_pos_test(cfg.PortOpt, None, [], None)
+
+    def test_positional_port_default(self):
+        self._do_pos_test(cfg.PortOpt, 80, [], 80)
+
+    def test_positional_port_arg(self):
+        self._do_pos_test(cfg.PortOpt, None, ['443'], 443)
+
     def test_positional_multistr_none_default(self):
         self._do_pos_test(cfg.MultiStrOpt, None, [], None)
 
@@ -833,6 +869,15 @@
     def test_conf_file_str_ignore_dgroup_and_dname(self):
         self._do_dgroup_and_dname_test_ignore(cfg.StrOpt, 'value2', 'value2')
 
+    def test_conf_file_str_value_with_good_choice_value(self):
+        self.conf.register_opt(cfg.StrOpt('foo', choices=['bar', 'baz']))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = bar\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual(self.conf.foo, 'bar')
+
     def test_conf_file_bool_default(self):
         self.conf.register_opt(cfg.BoolOpt('foo', default=False))
 
@@ -943,10 +988,10 @@
         self.assertTrue(hasattr(self.conf, 'foo'))
         self.assertEqual(self.conf.foo, 666)
 
-    @mock.patch.object(cfg, 'LOG')
-    def test_conf_file_int_wrong_default(self, mock_log):
-        cfg.IntOpt('foo', default='666')
-        self.assertEqual(1, mock_log.debug.call_count)
+    def test_conf_file_int_string_default_type(self):
+        self.conf.register_opt(cfg.IntOpt('foo', default='666'))
+        self.conf([])
+        self.assertEqual(self.conf.foo, 666)
 
     def test_conf_file_int_value(self):
         self.conf.register_opt(cfg.IntOpt('foo'))
@@ -1022,10 +1067,9 @@
         self.assertTrue(hasattr(self.conf, 'foo'))
         self.assertEqual(self.conf.foo, 6.66)
 
-    @mock.patch.object(cfg, 'LOG')
-    def test_conf_file_float_default_wrong_type(self, mock_log):
-        cfg.FloatOpt('foo', default='foobar6.66')
-        self.assertEqual(1, mock_log.debug.call_count)
+    def test_conf_file_float_default_wrong_type(self):
+        self.assertRaises(cfg.DefaultValueError, cfg.FloatOpt, 'foo',
+                          default='foobar6.66')
 
     def test_conf_file_float_value(self):
         self.conf.register_opt(cfg.FloatOpt('foo'))
@@ -1088,15 +1132,9 @@
         self.assertTrue(hasattr(self.conf, 'foo'))
         self.assertEqual(self.conf.foo, ['bar'])
 
-    @mock.patch.object(cfg, 'LOG')
-    def test_conf_file_list_default_wrong_type(self, mock_log):
-        cfg.ListOpt('foo', default=25)
-        mock_log.debug.assert_called_once_with(
-            'Expected default value of type(s) %(extypes)s but '
-            'got %(default)r of type %(deftypes)s',
-            {'extypes': 'list',
-             'default': 25,
-             'deftypes': 'int'})
+    def test_conf_file_list_default_wrong_type(self):
+        self.assertRaises(cfg.DefaultValueError, cfg.ListOpt, 'foo',
+                          default=25)
 
     def test_conf_file_list_value(self):
         self.conf.register_opt(cfg.ListOpt('foo'))
@@ -1127,6 +1165,31 @@
         self.assertTrue(hasattr(self.conf, 'foo'))
         self.assertEqual(self.conf.foo, ['b', 'a', 'r'])
 
+    def test_conf_file_list_item_type(self):
+        self.conf.register_cli_opt(cfg.ListOpt('foo',
+                                               item_type=types.Integer()))
+
+        paths = self.create_tempfiles([('1',
+                                        '[DEFAULT]\n'
+                                        'foo = 1,2\n')])
+
+        self.conf(['--config-file', paths[0]])
+
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual([1, 2], self.conf.foo)
+
+    def test_conf_file_list_item_wrong_type(self):
+        self.assertRaises(cfg.DefaultValueError, cfg.ListOpt, 'foo',
+                          default="bar", item_type=types.Integer())
+
+    def test_conf_file_list_bounds(self):
+        self.conf.register_cli_opt(cfg.ListOpt('foo',
+                                               item_type=types.Integer(),
+                                               default="[1,2]",
+                                               bounds=True))
+        self.conf([])
+        self.assertEqual(self.conf.foo, [1, 2])
+
     def test_conf_file_list_use_dname(self):
         self._do_dname_test_use(cfg.ListOpt, 'a,b,c', ['a', 'b', 'c'])
 
@@ -1219,7 +1282,7 @@
         self.conf(['--config-file', paths[0]])
 
         self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo')
-        self.assertRaises(AttributeError, getattr, self.conf, 'foo')
+        self.assertRaises(ValueError, getattr, self.conf, 'foo')
 
     def test_conf_file_dict_value_duplicate_key(self):
         self.conf.register_opt(cfg.DictOpt('foo'))
@@ -1231,7 +1294,7 @@
         self.conf(['--config-file', paths[0]])
 
         self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo')
-        self.assertRaises(AttributeError, getattr, self.conf, 'foo')
+        self.assertRaises(ValueError, getattr, self.conf, 'foo')
 
     def test_conf_file_dict_values_override_deprecated(self):
         self.conf.register_cli_opt(cfg.DictOpt('foo',
@@ -1356,6 +1419,16 @@
                                                'k2': 'e',
                                                'k3': 'f'})
 
+    def test_conf_file_port_outside_range(self):
+        self.conf.register_opt(cfg.PortOpt('foo'))
+
+        paths = self.create_tempfiles([('test',
+                                        '[DEFAULT]\n'
+                                        'foo = 65536\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo')
+
     def test_conf_file_multistr_default(self):
         self.conf.register_opt(cfg.MultiStrOpt('foo', default=['bar']))
 
@@ -2302,6 +2375,30 @@
         self.assertTrue(hasattr(self.conf.snafu, 'bell'))
         self.assertEqual(self.conf.snafu.bell, 'whistle-02')
 
+    def test_config_dir_multistr(self):
+        # Demonstrate that values for multistr options found in
+        # different sources are combined.
+        self.conf.register_cli_opt(cfg.MultiStrOpt('foo'))
+
+        dir = tempfile.mkdtemp()
+        self.tempdirs.append(dir)
+
+        paths = self.create_tempfiles([(os.path.join(dir, '00-test'),
+                                        '[DEFAULT]\n'
+                                        'foo = bar-00\n'),
+                                       (os.path.join(dir, '02-test'),
+                                        '[DEFAULT]\n'
+                                        'foo = bar-02\n'),
+                                       (os.path.join(dir, '01-test'),
+                                        '[DEFAULT]\n'
+                                        'foo = bar-01\n')])
+
+        self.conf(['--foo', 'bar',
+                   '--config-dir', os.path.dirname(paths[0])])
+
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual(['bar', 'bar-00', 'bar-01', 'bar-02'], self.conf.foo)
+
     def test_config_dir_file_precedence(self):
         snafu_group = cfg.OptGroup('snafu')
         self.conf.register_group(snafu_group)
@@ -2879,7 +2976,7 @@
 
         self.conf(['--config-file', paths[0]])
 
-        self.assertRaises(AttributeError, getattr, self.conf, 'foo')
+        self.assertRaises(ValueError, getattr, self.conf, 'foo')
         self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo')
 
     def test_conf_file_bad_bool(self):
@@ -3592,7 +3689,7 @@
         self.conf(['--config-file', paths[0]])
 
         self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo')
-        self.assertRaises(AttributeError, getattr, self.conf, 'foo')
+        self.assertRaises(ValueError, getattr, self.conf, 'foo')
 
     def test_conf_file_choice_value_override(self):
         self.conf.register_cli_opt(cfg.StrOpt('foo',
@@ -3613,14 +3710,158 @@
         self.assertEqual(self.conf.foo, 'baaar')
 
     def test_conf_file_choice_bad_default(self):
+        self.assertRaises(cfg.DefaultValueError, cfg.StrOpt, 'foo',
+                          choices=['baar', 'baaar'], default='foobaz')
+
+
+class RegexTestCase(BaseTestCase):
+
+    def test_regex_good(self):
         self.conf.register_cli_opt(cfg.StrOpt('foo',
-                                              choices=['baar', 'baaar'],
-                                              default='foobaz'))
-        self.conf([])
-        self.assertRaises(AttributeError,
-                          getattr,
-                          self.conf,
-                          'foobaz')
+                                              regex='foo|bar'))
+        self.conf(['--foo', 'bar'])
+        self.assertEqual(self.conf.foo, 'bar')
+        self.conf(['--foo', 'foo'])
+        self.assertEqual(self.conf.foo, 'foo')
+        self.conf(['--foo', 'foobar'])
+        self.assertEqual(self.conf.foo, 'foobar')
+
+    def test_regex_bad(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo',
+                                              regex='bar'))
+        self.assertRaises(SystemExit, self.conf, ['--foo', 'foo'])
+
+    def test_conf_file_regex_value(self):
+        self.conf.register_opt(cfg.StrOpt('foo',
+                                          regex='bar'))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = bar\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual(self.conf.foo, 'bar')
+
+    def test_conf_file_regex_bad_value(self):
+        self.conf.register_opt(cfg.StrOpt('foo',
+                                          regex='bar'))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = other\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertRaisesRegex(cfg.ConfigFileValueError, "doesn't match regex",
+                               self.conf._get, 'foo')
+        self.assertRaisesRegex(ValueError, "doesn't match regex",
+                               getattr, self.conf, 'foo')
+
+    def test_regex_with_choice(self):
+        self.assertRaises(ValueError, cfg.StrOpt,
+                          'foo', choices=['bar1'], regex='bar2')
+
+
+class QuotesTestCase(BaseTestCase):
+
+    def test_quotes_good(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo',
+                                              quotes=True))
+        self.conf(['--foo', '"foobar1"'])
+        self.assertEqual(self.conf.foo, 'foobar1')
+        self.conf(['--foo', "'foobar2'"])
+        self.assertEqual(self.conf.foo, 'foobar2')
+        self.conf(['--foo', 'foobar3'])
+        self.assertEqual(self.conf.foo, 'foobar3')
+        self.conf(['--foo', 'foobar4"'])
+        self.assertEqual(self.conf.foo, 'foobar4"')
+
+    def test_quotes_bad(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo',
+                                              quotes=True))
+        self.assertRaises(SystemExit, self.conf, ['--foo', '"foobar\''])
+        self.assertRaises(SystemExit, self.conf, ['--foo', '\'foobar"'])
+        self.assertRaises(SystemExit, self.conf, ['--foo', '"foobar'])
+        self.assertRaises(SystemExit, self.conf, ['--foo', "'foobar"])
+
+    def test_conf_file_quotes_good_value(self):
+        self.conf.register_opt(cfg.StrOpt('foo',
+                                          quotes=True))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = "bar"\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual(self.conf.foo, 'bar')
+
+    def test_conf_file_quotes_bad_value(self):
+        self.conf.register_opt(cfg.StrOpt('foo',
+                                          quotes=True))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = "bar\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertRaisesRegex(cfg.ConfigFileValueError, 'Non-closed quote:',
+                               self.conf._get, 'foo')
+        self.assertRaisesRegex(ValueError, 'Non-closed quote:',
+                               getattr, self.conf, 'foo')
+
+
+class IgnoreCaseTestCase(BaseTestCase):
+
+    def test_ignore_case_with_choices(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo',
+                                              ignore_case=True,
+                                              choices=['bar1',
+                                                       'bar2',
+                                                       'BAR3']))
+        self.conf(['--foo', 'bAr1'])
+        self.assertEqual(self.conf.foo, 'bAr1')
+        self.conf(['--foo', 'BaR2'])
+        self.assertEqual(self.conf.foo, 'BaR2')
+        self.conf(['--foo', 'baR3'])
+        self.assertEqual(self.conf.foo, 'baR3')
+
+    def test_ignore_case_with_regex(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo',
+                                              ignore_case=True,
+                                              regex='fOO|bar'))
+        self.conf(['--foo', 'foo'])
+        self.assertEqual(self.conf.foo, 'foo')
+        self.conf(['--foo', 'Bar'])
+        self.assertEqual(self.conf.foo, 'Bar')
+        self.conf(['--foo', 'FOObar'])
+        self.assertEqual(self.conf.foo, 'FOObar')
+
+    def test_conf_file_ignore_case_with_choices(self):
+        self.conf.register_opt(cfg.StrOpt('foo',
+                                          ignore_case=True,
+                                          choices=['bar1', 'bar2', 'BAR3']))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = bAr2\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual(self.conf.foo, 'bAr2')
+
+    def test_conf_file_ignore_case_with_regex(self):
+        self.conf.register_opt(cfg.StrOpt('foo',
+                                          ignore_case=True,
+                                          regex='bAr'))
+
+        paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = BaR\n')])
+
+        self.conf(['--config-file', paths[0]])
+        self.assertTrue(hasattr(self.conf, 'foo'))
+        self.assertEqual(self.conf.foo, 'BaR')
+
+
+class StrOptMaxLengthTestCase(BaseTestCase):
+
+    def test_stropt_max_length_good(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo', max_length=5))
+        self.conf(['--foo', '12345'])
+        self.assertEqual(self.conf.foo, '12345')
+
+    def test_stropt_max_length_bad(self):
+        self.conf.register_cli_opt(cfg.StrOpt('foo', max_length=5))
+        self.assertRaises(SystemExit, self.conf, ['--foo', '123456'])
 
 
 class PrintHelpTestCase(base.BaseTestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/oslo.config-2.3.0/oslo_config/tests/test_generator.py 
new/oslo.config-2.7.0/oslo_config/tests/test_generator.py
--- old/oslo.config-2.3.0/oslo_config/tests/test_generator.py   2015-08-24 
23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/oslo_config/tests/test_generator.py   2015-11-16 
21:22:43.000000000 +0100
@@ -104,6 +104,12 @@
         'dict_opt': cfg.DictOpt('dict_opt',
                                 default={'1': 'yes', '2': 'no'},
                                 help='a dict'),
+        'ip_opt': cfg.IPOpt('ip_opt',
+                            default='127.0.0.1',
+                            help='an ip address'),
+        'port_opt': cfg.PortOpt('port_opt',
+                                default=80,
+                                help='a port'),
         'multi_opt': cfg.MultiStrOpt('multi_opt',
                                      default=['1', '2', '3'],
                                      help='multiple strings'),
@@ -472,6 +478,30 @@
 # a dict (dict value)
 #dict_opt = 1:yes,2:no
 ''')),
+        ('ip_opt',
+         dict(opts=[('test', [(None, [opts['ip_opt']])])],
+              expected='''[DEFAULT]
+
+#
+# From test
+#
+
+# an ip address (ip address value)
+#ip_opt = 127.0.0.1
+''')),
+        ('port_opt',
+         dict(opts=[('test', [(None, [opts['port_opt']])])],
+              expected='''[DEFAULT]
+
+#
+# From test
+#
+
+# a port (port value)
+# Minimum value: 1
+# Maximum value: 65535
+#port_opt = 80
+''')),
         ('multi_opt',
          dict(opts=[('test', [(None, [opts['multi_opt']])])],
               expected='''[DEFAULT]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo_config/tests/test_types.py 
new/oslo.config-2.7.0/oslo_config/tests/test_types.py
--- old/oslo.config-2.3.0/oslo_config/tests/test_types.py       2015-08-24 
23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/oslo_config/tests/test_types.py       2015-11-16 
21:22:43.000000000 +0100
@@ -126,6 +126,36 @@
         t2 = types.String(regex=re.compile("^[a-z]"))
         self.assertFalse(t1 == t2)
 
+    def test_ignore_case(self):
+        self.type_instance = types.String(choices=['foo', 'bar'],
+                                          ignore_case=True)
+        self.assertConvertedValue('Foo', 'Foo')
+        self.assertConvertedValue('bAr', 'bAr')
+
+    def test_ignore_case_raises(self):
+        self.type_instance = types.String(choices=['foo', 'bar'],
+                                          ignore_case=False)
+        self.assertRaises(ValueError, self.assertConvertedValue, 'Foo', 'Foo')
+
+    def test_regex_and_ignore_case(self):
+        self.type_instance = types.String(regex=re.compile("^[A-Z]"),
+                                          ignore_case=True)
+        self.assertConvertedValue("foo", "foo")
+
+    def test_regex_and_ignore_case_str(self):
+        self.type_instance = types.String(regex="^[A-Z]", ignore_case=True)
+        self.assertConvertedValue("foo", "foo")
+
+    def test_regex_preserve_flags(self):
+        self.type_instance = types.String(regex=re.compile("^[A-Z]", re.I),
+                                          ignore_case=False)
+        self.assertConvertedValue("foo", "foo")
+
+    def test_max_length(self):
+        self.type_instance = types.String(max_length=5)
+        self.assertInvalid('123456')
+        self.assertConvertedValue('12345', '12345')
+
 
 class BooleanTypeTests(TypeTestHelper, unittest.TestCase):
     type = types.Boolean()
@@ -209,6 +239,8 @@
     def test_repr_with_min_and_max(self):
         t = types.Integer(min=123, max=456)
         self.assertEqual('Integer(min=123, max=456)', repr(t))
+        t = types.Integer(min=0, max=0)
+        self.assertEqual('Integer(min=0, max=0)', repr(t))
 
     def test_equal(self):
         self.assertTrue(types.Integer() == types.Integer())
@@ -230,6 +262,20 @@
     def test_not_equal_to_other_class(self):
         self.assertFalse(types.Integer() == types.String())
 
+    def test_min_greater_max(self):
+        self.assertRaises(ValueError,
+                          types.Integer,
+                          min=100, max=50)
+        self.assertRaises(ValueError,
+                          types.Integer,
+                          min=-50, max=-100)
+        self.assertRaises(ValueError,
+                          types.Integer,
+                          min=0, max=-50)
+        self.assertRaises(ValueError,
+                          types.Integer,
+                          min=50, max=0)
+
     def test_with_max_and_min(self):
         t = types.Integer(min=123, max=456)
         self.assertRaises(ValueError, t, 122)
@@ -239,6 +285,26 @@
         self.assertRaises(ValueError, t, 0)
         self.assertRaises(ValueError, t, 457)
 
+    def test_with_min_zero(self):
+        t = types.Integer(min=0, max=456)
+        self.assertRaises(ValueError, t, -1)
+        t(0)
+        t(123)
+        t(300)
+        t(456)
+        self.assertRaises(ValueError, t, -201)
+        self.assertRaises(ValueError, t, 457)
+
+    def test_with_max_zero(self):
+        t = types.Integer(min=-456, max=0)
+        self.assertRaises(ValueError, t, 1)
+        t(0)
+        t(-123)
+        t(-300)
+        t(-456)
+        self.assertRaises(ValueError, t, 201)
+        self.assertRaises(ValueError, t, -457)
+
 
 class FloatTypeTests(TypeTestHelper, unittest.TestCase):
     type = types.Float()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/oslo_config/types.py 
new/oslo.config-2.7.0/oslo_config/types.py
--- old/oslo.config-2.3.0/oslo_config/types.py  2015-08-24 23:32:24.000000000 
+0200
+++ new/oslo.config-2.7.0/oslo_config/types.py  2015-11-16 21:22:43.000000000 
+0100
@@ -17,6 +17,7 @@
 Use these classes as values for the `type` argument to
 :class:`oslo_config.cfg.Opt` and its subclasses.
 
+.. versionadded:: 1.3
 """
 import re
 
@@ -25,11 +26,7 @@
 
 
 class ConfigType(object):
-
-    BASE_TYPES = (None,)
-
-    def is_base_type(self, other):
-        return isinstance(other, self.BASE_TYPES)
+    pass
 
 
 class String(ConfigType):
@@ -48,18 +45,48 @@
     :param regex: Optional regular expression (string or compiled
                   regex) that the value must match on an unanchored
                   search. Mutually exclusive with 'choices'.
-    """
+    :param ignore_case:  If True case differences (uppercase vs. lowercase)
+                         between 'choices' or 'regex' will be ignored;
+                         defaults to False.
+    :param max_length:  Optional integer. If a positive value is specified,
+                        a maximum length of an option value must be less than
+                        or equal to this parameter. Otherwise no length check
+                        will be done.
 
-    BASE_TYPES = six.string_types
+    .. versionchanged:: 2.1
+       Added *regex* parameter.
+
+    .. versionchanged:: 2.5
+       Added *ignore_case* parameter.
+
+    .. versionchanged:: 2.7
+       Added *max_length* parameter.
+    """
 
-    def __init__(self, choices=None, quotes=False, regex=None):
+    def __init__(self, choices=None, quotes=False, regex=None,
+                 ignore_case=False, max_length=None):
         super(String, self).__init__()
         if choices and regex:
             raise ValueError("'choices' and 'regex' cannot both be specified")
 
-        self.choices = choices
+        self.ignore_case = ignore_case
         self.quotes = quotes
-        self.regex = re.compile(regex) if regex is not None else None
+        self.max_length = max_length or 0
+
+        self.choices = choices
+        self.lower_case_choices = None
+        if self.choices is not None and self.ignore_case:
+            self.lower_case_choices = [c.lower() for c in choices]
+
+        self.regex = regex
+        if self.regex is not None:
+            re_flags = re.IGNORECASE if self.ignore_case else 0
+
+            # Check if regex is a string or an already compiled regex
+            if isinstance(regex, six.string_types):
+                self.regex = re.compile(regex, re_flags)
+            else:
+                self.regex = re.compile(regex.pattern, re_flags | regex.flags)
 
     def __call__(self, value):
         value = str(value)
@@ -69,11 +96,22 @@
                     raise ValueError('Non-closed quote: %s' % value)
                 value = value[1:-1]
 
+        if self.max_length > 0 and len(value) > self.max_length:
+            raise ValueError("Value '%s' exceeds maximum length %d" %
+                             (value, self.max_length))
+
         if self.regex and not self.regex.search(value):
             raise ValueError("Value %r doesn't match regex %r" %
                              (value, self.regex.pattern))
 
-        if self.choices is None or value in self.choices:
+        if self.choices is None:
+            return value
+
+        # Check for case insensitive
+        processed_value, choices = ((value.lower(), self.lower_case_choices)
+                                    if self.ignore_case else
+                                    (value, self.choices))
+        if processed_value in choices:
             return value
 
         raise ValueError(
@@ -101,8 +139,7 @@
 
 
 class MultiString(String):
-
-    BASE_TYPES = six.string_types + (list,)
+    pass
 
 
 class Boolean(ConfigType):
@@ -115,8 +152,6 @@
     TRUE_VALUES = ['true', '1', 'on', 'yes']
     FALSE_VALUES = ['false', '0', 'off', 'no']
 
-    BASE_TYPES = (bool,)
-
     def __call__(self, value):
         if isinstance(value, bool):
             return value
@@ -145,15 +180,16 @@
 
     :param min: Optional check that value is greater than or equal to min
     :param max: Optional check that value is less than or equal to max
-    """
 
-    BASE_TYPES = six.integer_types
+    .. versionchanged:: 2.4
+       The class now honors zero for *min* and *max* parameters.
+    """
 
     def __init__(self, min=None, max=None):
         super(Integer, self).__init__()
         self.min = min
         self.max = max
-        if min and max and max < min:
+        if min is not None and max is not None and max < min:
             raise ValueError('Max value is less than min value')
 
     def __call__(self, value):
@@ -170,17 +206,17 @@
         return value
 
     def _check_range(self, value):
-        if self.min and value < self.min:
+        if self.min is not None and value < self.min:
             raise ValueError('Should be greater than or equal to %d' %
                              self.min)
-        if self.max and value > self.max:
+        if self.max is not None and value > self.max:
             raise ValueError('Should be less than or equal to %d' % self.max)
 
     def __repr__(self):
         props = []
-        if self.min:
+        if self.min is not None:
             props.append('min=%d' % self.min)
-        if self.max:
+        if self.max is not None:
             props.append('max=%d' % self.max)
 
         if props:
@@ -199,9 +235,6 @@
 
     """Float type."""
 
-    # allow float to be set from int
-    BASE_TYPES = six.integer_types + (float,)
-
     def __call__(self, value):
         if isinstance(value, float):
             return value
@@ -231,8 +264,6 @@
     :param bounds: if True, value should be inside "[" and "]" pair
     """
 
-    BASE_TYPES = (list,)
-
     def __init__(self, item_type=None, bounds=False):
         super(List, self).__init__()
 
@@ -304,8 +335,6 @@
     :param bounds: if True, value should be inside "{" and "}" pair
     """
 
-    BASE_TYPES = (dict,)
-
     def __init__(self, value_type=None, bounds=False):
         super(Dict, self).__init__()
 
@@ -392,8 +421,6 @@
 
     """
 
-    BASE_TYPES = six.string_types
-
     def __init__(self, version=None):
         super(IPAddress, self).__init__()
         version_checkers = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/requirements.txt 
new/oslo.config-2.7.0/requirements.txt
--- old/oslo.config-2.3.0/requirements.txt      2015-08-24 23:32:24.000000000 
+0200
+++ new/oslo.config-2.7.0/requirements.txt      2015-11-16 21:22:43.000000000 
+0100
@@ -3,6 +3,6 @@
 # process, which may cause wedges in the gate later.
 
 argparse
-netaddr>=0.7.12
+netaddr!=0.7.16,>=0.7.12
 six>=1.9.0
 stevedore>=1.5.0 # Apache-2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/setup.cfg 
new/oslo.config-2.7.0/setup.cfg
--- old/oslo.config-2.3.0/setup.cfg     2015-08-24 23:32:57.000000000 +0200
+++ new/oslo.config-2.7.0/setup.cfg     2015-11-16 21:23:02.000000000 +0100
@@ -5,7 +5,7 @@
 summary = Oslo Configuration API
 description-file = 
        README.rst
-home-page = https://launchpad.net/oslo
+home-page = https://wiki.openstack.org/wiki/Oslo#oslo.config
 classifier = 
        Development Status :: 4 - Beta
        Environment :: OpenStack
@@ -40,11 +40,14 @@
 [upload_sphinx]
 upload-dir = doc/build/html
 
+[pbr]
+warnerrors = True
+
 [wheel]
 universal = 1
 
 [egg_info]
-tag_build = 
 tag_date = 0
 tag_svn_revision = 0
+tag_build = 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/setup.py 
new/oslo.config-2.7.0/setup.py
--- old/oslo.config-2.3.0/setup.py      2015-08-24 23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/setup.py      2015-11-16 21:22:43.000000000 +0100
@@ -25,5 +25,5 @@
     pass
 
 setuptools.setup(
-    setup_requires=['pbr>=1.3'],
+    setup_requires=['pbr>=1.8'],
     pbr=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/oslo.config-2.3.0/tox.ini 
new/oslo.config-2.7.0/tox.ini
--- old/oslo.config-2.3.0/tox.ini       2015-08-24 23:32:24.000000000 +0200
+++ new/oslo.config-2.7.0/tox.ini       2015-11-16 21:22:43.000000000 +0100
@@ -14,7 +14,7 @@
 [testenv:cover]
 setenv = VIRTUAL_ENV={envdir}
 commands =
-  python setup.py testr --coverage
+  python setup.py test --coverage --coverage-package-name=oslo_config 
--testr-args='{posargs}'
 
 [testenv:venv]
 commands = {posargs}



Reply via email to