diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 3240e0f..0000000 --- a/.coveragerc +++ /dev/null @@ -1,8 +0,0 @@ -[run] -branch = True -source = oslo_config -omit = oslo_config/tests/* - -[report] -ignore_errors = True -precision = 2 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 5d439ea..0000000 --- a/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -AUTHORS -ChangeLog -*~ -*.egg* -*.swp -*.pyc -*.log -.tox -.coverage* -cover -oslo.config.egg-info/ -build/ -doc/build/ -doc/source/api/ -dist/ -.testrepository/ -.project -.pydevproject -/doc/source/sample.config - -# Files created by releasenotes build -releasenotes/build diff --git a/.gitreview b/.gitreview deleted file mode 100644 index f5a3cc8..0000000 --- a/.gitreview +++ /dev/null @@ -1,4 +0,0 @@ -[gerrit] -host=review.openstack.org -port=29418 -project=openstack/oslo.config.git diff --git a/.testr.conf b/.testr.conf deleted file mode 100644 index 1641f86..0000000 --- a/.testr.conf +++ /dev/null @@ -1,4 +0,0 @@ -[DEFAULT] -test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 OS_TEST_TIMEOUT=60 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION -test_id_option=--load-list $IDFILE -test_list_option=--list diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index ed43617..0000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,16 +0,0 @@ -If you would like to contribute to the development of OpenStack, -you must follow the steps in this page: - - http://docs.openstack.org/infra/manual/developers.html - -Once those steps have been completed, changes to OpenStack -should be submitted for review via the Gerrit tool, following -the workflow documented at: - - http://docs.openstack.org/infra/manual/developers.html#development-workflow - -Pull requests submitted through GitHub will be ignored. - -Bugs should be filed on Launchpad, not GitHub: - - https://bugs.launchpad.net/oslo.config diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 4143aac..0000000 --- a/LICENSE +++ /dev/null @@ -1,204 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - ---- License for python-keystoneclient versions prior to 2.1 --- - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of this project nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.rst b/README.rst deleted file mode 100644 index 26ee950..0000000 --- a/README.rst +++ /dev/null @@ -1,19 +0,0 @@ -========================== -Oslo Configuration Library -========================== - -.. image:: https://img.shields.io/pypi/v/oslo.config.svg - :target: https://pypi.python.org/pypi/oslo.config/ - :alt: Latest Version - -.. image:: https://img.shields.io/pypi/dm/oslo.config.svg - :target: https://pypi.python.org/pypi/oslo.config/ - :alt: Downloads - -The Oslo configuration API supports parsing command line arguments and -.ini style configuration files. - -* License: Apache License, Version 2.0 -* Documentation: http://docs.openstack.org/developer/oslo.config -* Source: http://git.openstack.org/cgit/openstack/oslo.config -* Bugs: http://bugs.launchpad.net/oslo.config diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..2e2938d --- /dev/null +++ b/README.txt @@ -0,0 +1,13 @@ +This project is no longer maintained. + +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". + +Use instead the project deb-python-oslo.config at +http://git.openstack.org/cgit/openstack/deb-python-oslo.config . + +For any further questions, please email +openstack-dev@lists.openstack.org or join #openstack-dev on +Freenode. diff --git a/doc/source/builtins.rst b/doc/source/builtins.rst deleted file mode 100644 index fc80f74..0000000 --- a/doc/source/builtins.rst +++ /dev/null @@ -1,5 +0,0 @@ -================== - Built-in Options -================== - -.. show-options:: oslo.config diff --git a/doc/source/cfg.rst b/doc/source/cfg.rst deleted file mode 100644 index 5e3f988..0000000 --- a/doc/source/cfg.rst +++ /dev/null @@ -1,5 +0,0 @@ --------------- -The cfg Module --------------- - -.. automodule:: oslo_config.cfg diff --git a/doc/source/cfgfilter.rst b/doc/source/cfgfilter.rst deleted file mode 100644 index c11cddd..0000000 --- a/doc/source/cfgfilter.rst +++ /dev/null @@ -1,5 +0,0 @@ --------------------- -The cfgfilter Module --------------------- - -.. automodule:: oslo_config.cfgfilter diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 2335ba2..0000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,78 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -import subprocess -import sys -import warnings - -sys.path.insert(0, os.path.abspath('../..')) -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'oslosphinx', - 'oslo_config.sphinxconfiggen', - 'oslo_config.sphinxext'] - -config_generator_config_file = 'config-generator.conf' - -# autodoc generation is a bit aggressive and a nuisance when doing heavy -# text edit cycles. -# execute "export SPHINX_DEBUG=1" in your terminal to disable - -# Add any paths that contain templates here, relative to this directory. -# templates_path = [] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'oslo.config' -copyright = u'2013, OpenStack Foundation' - -# If true, '()' will be appended to :func: etc. cross-reference text. -add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = True - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['oslo_config.'] - - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -# html_theme = '_theme' -html_static_path = ['static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = '%sdoc' % project - -git_cmd = ["git", "log", "--pretty=format:'%ad, commit %h'", "--date=local", - "-n1"] -try: - html_last_updated_fmt = subprocess.Popen( - git_cmd, stdout=subprocess.PIPE).communicate()[0].decode('utf-8') -except Exception: - warnings.warn('Cannot get last updated time from git repository. ' - 'Not setting "html_last_updated_fmt".') - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', - '%s.tex' % project, - '%s Documentation' % project, - 'OpenStack Foundation', 'manual'), -] diff --git a/doc/source/config-generator.conf b/doc/source/config-generator.conf deleted file mode 100644 index 0c67e40..0000000 --- a/doc/source/config-generator.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -wrap_width = 79 -namespace = oslo.config diff --git a/doc/source/configopts.rst b/doc/source/configopts.rst deleted file mode 100644 index 06c9c3b..0000000 --- a/doc/source/configopts.rst +++ /dev/null @@ -1,8 +0,0 @@ --------------------- -The ConfigOpts Class --------------------- - -.. currentmodule:: oslo_config.cfg - -.. autoclass:: ConfigOpts - :members: diff --git a/doc/source/contributing.rst b/doc/source/contributing.rst deleted file mode 100644 index 2ca75d1..0000000 --- a/doc/source/contributing.rst +++ /dev/null @@ -1,5 +0,0 @@ -============== - Contributing -============== - -.. include:: ../../CONTRIBUTING.rst diff --git a/doc/source/exceptions.rst b/doc/source/exceptions.rst deleted file mode 100644 index eaf5f49..0000000 --- a/doc/source/exceptions.rst +++ /dev/null @@ -1,16 +0,0 @@ ----------- -Exceptions ----------- - -.. currentmodule:: oslo_config.cfg - -.. autoexception:: Error -.. autoexception:: ArgsAlreadyParsedError -.. autoexception:: NoSuchOptError -.. autoexception:: NoSuchGroupError -.. autoexception:: DuplicateOptError -.. autoexception:: RequiredOptError -.. autoexception:: TemplateSubstitutionError -.. autoexception:: ConfigFilesNotFoundError -.. autoexception:: ConfigFileParseError -.. autoexception:: ConfigFileValueError diff --git a/doc/source/faq.rst b/doc/source/faq.rst deleted file mode 100644 index 2171d13..0000000 --- a/doc/source/faq.rst +++ /dev/null @@ -1,98 +0,0 @@ -============================ - Frequently Asked Questions -============================ - -Why does oslo.config have a CONF object? Global objects SUCK! -============================================================= - -.. original source: https://wiki.openstack.org/wiki/Oslo#Why_does_oslo.config_have_a_CONF_object.3F_Global_object_SUCK.21 - -Indeed. Well, it's a long story and well documented in mailing list -archives if anyone cares to dig up some links. - -Around the time of the Folsom Design Summit, an attempt was made to -remove our dependence on a global object like this. There was massive -debate and, in the end, the rough consensus was to stick with using -this approach. - -Nova, through its use of the gflags library, used this approach from -`commit zero -`__. Some -OpenStack projects didn't initially use this approach, but most now -do. The idea is that having all projects use the same approach is more -important than the objections to the approach. Sharing code between -projects is great, but by also having projects use the same idioms for -stuff like this it makes it much easier for people to work on multiple -projects. - -This debate will probably never completely go away, though. See `this -latest discussion in August, 2014 -`__ - -Why are configuration options not part of a library's API? -========================================================== - -Configuration options are a way for deployers to change the behavior -of OpenStack. Applications are not supposed to be aware of the -configuration options defined and used within libraries, because the -library API is supposed to work transparently no matter which backend -is configured. - -Configuration options in libraries can be renamed, moved, and -deprecated just like configuration options in applications. However, -if applications are allowed to read or write the configuration options -directly, treating them as an API, the option cannot be renamed -without breaking the application. Instead, libraries should provide a -programmatic API (usually a :func:`set_defaults` function) for setting -the defaults for configuration options. For example, this function -from ``oslo.log`` lets the caller change the format string and default -logging levels: - -:: - - def set_defaults(logging_context_format_string=None, - default_log_levels=None): - """Set default values for the configuration options used by oslo.log.""" - # Just in case the caller is not setting the - # default_log_level. This is insurance because - # we introduced the default_log_level parameter - # later in a backwards in-compatible change - if default_log_levels is not None: - cfg.set_defaults( - _options.log_opts, - default_log_levels=default_log_levels) - if logging_context_format_string is not None: - cfg.set_defaults( - _options.log_opts, - logging_context_format_string=logging_context_format_string) - -If the name of either option changes, the API of :func:`set_defaults` -can be updated to allow both names, and warn if the old one is -provided. Using a supported API like this is better than having an -application call :func:`set_default` on the configuration object -directly, such as: - -:: - - cfg.CONF.set_default('default_log_levels', default_log_levels) - -This form will trigger an error if the logging options are moved out -of the default option group into their own section of the -configuration file. It will also fail if the ``default_log_levels`` -option is not yet registered, or if it is renamed. All of those cases -can be protected against with a :func:`set_defaults` function in the -library that owns the options. - -Similarly, code that does not *own* the configuration option -definition should not read the option value. An application should -never, for example, do something like: - -:: - - log_file = cfg.CONF.log_file - -The type, name, and existence of the ``log_file`` configuration option -is subject to change. ``oslo.config`` makes it easy to communicate -that change to a deployer in a way that allows their old configuration -files to continue to work. It has no mechanism for doing that in -application code, however. diff --git a/doc/source/fixture.rst b/doc/source/fixture.rst deleted file mode 100644 index 47c4939..0000000 --- a/doc/source/fixture.rst +++ /dev/null @@ -1,8 +0,0 @@ ------------- -Test Fixture ------------- - -.. currentmodule:: oslo_config.fixture - -.. autoclass:: Config - :members: diff --git a/doc/source/generator.rst b/doc/source/generator.rst deleted file mode 100644 index 111028b..0000000 --- a/doc/source/generator.rst +++ /dev/null @@ -1,195 +0,0 @@ -======================= - oslo-config-generator -======================= - -oslo-config-generator is a utility for generating sample config files. For -example, to generate a sample config file for oslo.messaging you would run:: - - $> oslo-config-generator --namespace oslo.messaging > oslo.messaging.conf - -This generated sample lists all of the available options, along with their help -string, type, deprecated aliases and defaults. - -To generate a sample config file for an application ``myapp`` that has -its own options and uses oslo.messaging, you can list both -namespaces:: - - $> oslo-config-generator --namespace myapp --namespace oslo.messaging > myapp.conf - -.. versionadded:: 1.4 - -Defining Option Discovery Entry Points --------------------------------------- - -The ``--namespace`` option specifies an entry point name registered under the -'oslo.config.opts' entry point namespace. For example, in oslo.messaging's -setup.cfg we have:: - - [entry_points] - oslo.config.opts = - oslo.messaging = oslo.messaging.opts:list_opts - -The callable referenced by the entry point should take no arguments and return -a list of (``group``, [opt_1, opt_2]) tuples, where ``group`` is either a -group name as a string or an ``OptGroup`` object. Passing the ``OptGroup`` -object allows the consumer of the ``list_opts`` method to access and publish -group help. An example, using both styles:: - - opts1 = [ - cfg.StrOpt('foo'), - cfg.StrOpt('bar'), - ] - - opts2 = [ - cfg.StrOpt('baz'), - ] - - baz_group = cfg.OptGroup(name='baz_group' - title='Baz group options', - help='Baz group help text') - cfg.CONF.register_group(baz_group) - - cfg.CONF.register_opts(opts1, group='blaa') - cfg.CONF.register_opts(opts2, group=baz_group) - - def list_opts(): - # Allows the generation of the help text for - # the baz_group OptGroup object. No help - # text is generated for the 'blaa' group. - return [('blaa', opts1), (baz_group, opts2)] - -.. note:: - - You should return the original options, not a copy, because the - default update hooks depend on the original option object being - returned. - -The module holding the entry point *must* be importable, even if the -dependencies of that module are not installed. For example, driver -modules that define options but have optional dependencies on -third-party modules must still be importable if those modules are not -installed. To accomplish this, the optional dependency can either be -imported using :func:`oslo.utils.importutils.try_import` or the option -definitions can be placed in a file that does not try to import the -optional dependency. - -Modifying Defaults from Other Namespaces ----------------------------------------- - -Occasionally applications need to override the defaults for options -defined in libraries. At runtime this is done using an API within the -library. Since the config generator cannot guarantee the order in -which namespaces will be imported, we can't ensure that application -code can change the option defaults before the generator loads the -options from a library. Instead, a separate optional processing hook -is provided for applications to register a function to update default -values after *all* options are loaded. - -The hooks are registered in a separate entry point namespace -(``oslo.config.opts.defaults``), using the same entry point name as -**the application's** ``list_opts()`` function. - -:: - - [entry_points] - oslo.config.opts.defaults = - keystone = keystone.common.config:update_opt_defaults - -.. warning:: - - Never, under any circumstances, register an entry point using a - name owned by another project. Doing so causes unexpected interplay - between projects within the config generator and will result in - failure to generate the configuration file or invalid values - showing in the sample. - - In this case, the name of the entry point for the default override - function *must* match the name of one of the entry points defining - options for the application in order to be detected and - used. Applications that have multple list_opts functions should use - one that is present in the inputs for the config generator where - the changed defaults need to appear. For example, if an application - defines ``foo.api`` to list the API-related options, and needs to - override the defaults in the ``oslo.middleware.cors`` library, the - application should register ``foo.api`` under - ``oslo.config.opts.defaults`` and point to a function within the - application code space that changes the defaults for - ``oslo.middleware.cors``. - -The update function should take no arguments. It should invoke the -public :func:`set_defaults` functions in any libraries for which it -has option defaults to override, just as the application does during -its normal startup process. - -:: - - from oslo_log import log - - def update_opt_defaults(): - log.set_defaults( - default_log_levels=log.get_default_log_levels() + ['noisy=WARN'], - ) - -Generating Multiple Sample Configs ----------------------------------- - -A single codebase might have multiple programs, each of which use a subset of -the total set of options registered by the codebase. In that case, you can -register multiple entry points:: - - [entry_points] - oslo.config.opts = - nova.common = nova.config:list_common_opts - nova.api = nova.config:list_api_opts - nova.compute = nova.config:list_compute_opts - -and generate a config file specific to each program:: - - $> oslo-config-generator --namespace oslo.messaging \ - --namespace nova.common \ - --namespace nova.api > nova-api.conf - $> oslo-config-generator --namespace oslo.messaging \ - --namespace nova.common \ - --namespace nova.compute > nova-compute.conf - -To make this more convenient, you can use config files to describe your config -files:: - - $> cat > config-generator/api.conf < cat > config-generator/compute.conf < oslo-config-generator --config-file config-generator/api.conf - $> oslo-config-generator --config-file config-generator/compute.conf - -Sample Default Values ---------------------- - -The default runtime values of configuration options are not always the most -suitable values to include in sample config files - for example, rather than -including the IP address or hostname of the machine where the config file -was generated, you might want to include something like '10.0.0.1'. To -facilitate this, options can be supplied with a 'sample_default' attribute:: - - cfg.StrOpt('base_dir' - default=os.getcwd(), - sample_default='/usr/lib/myapp') - -API ---- - -.. currentmodule:: oslo_config.generator - -.. autofunction:: main -.. autofunction:: generate -.. autofunction:: register_cli_opts diff --git a/doc/source/helpers.rst b/doc/source/helpers.rst deleted file mode 100644 index e2d5cb4..0000000 --- a/doc/source/helpers.rst +++ /dev/null @@ -1,8 +0,0 @@ ----------------- -Helper Functions ----------------- - -.. currentmodule:: oslo_config.cfg - -.. autofunction:: find_config_files -.. autofunction:: set_defaults diff --git a/doc/source/history.rst b/doc/source/history.rst deleted file mode 100644 index db8340b..0000000 --- a/doc/source/history.rst +++ /dev/null @@ -1,2 +0,0 @@ -.. include:: ../../ChangeLog - diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index af4ba20..0000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,46 +0,0 @@ -oslo.config -=========== - -An OpenStack library for parsing configuration options from the command -line and configuration files. - -Contents -======== - -.. toctree:: - :maxdepth: 2 - - cfg - opts - types - configopts - cfgfilter - helpers - fixture - parser - exceptions - namespaces - styleguide - mutable - generator - builtins - sphinxext - sphinxconfiggen - faq - contributing - -Release Notes -============= - -.. toctree:: - :maxdepth: 1 - - history - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/doc/source/mutable.rst b/doc/source/mutable.rst deleted file mode 100644 index ba89a16..0000000 --- a/doc/source/mutable.rst +++ /dev/null @@ -1,121 +0,0 @@ -Enabling your project for mutable config -======================================== - -As of OpenStack Newton, config options can be marked as 'mutable'. This means -they can be reloaded (usually via SIGHUP) at runtime, without a service -restart. However, each project has to be enabled before this will work and some -care needs to be taken over how each option is used before it can safely be -marked mutable. - -.. contents:: Table of Contents - :local: - - -Calling mutate_config_files ---------------------------- - -Config mutation is triggered by ``ConfigOpts#mutate_config_files`` being -called. Services launched with oslo.service get a signal handler on SIGHUP but -by default that calls the older ``ConfigOpts#reload_config_files`` method. To -get the new behaviour, we have to pass ``restart_method='mutate'``. For -example:: - - service.ProcessLauncher(CONF, restart_method='mutate') - -An example patch is here: https://review.openstack.org/#/c/280851 - -Some projects may call ``reload_config_files`` directly, in this case just -change that call to ``mutate_config_files``. If there is no signal handler or -you want to trigger reload by a different method, maybe via a web UI or -watching a file, just ensure your trigger calls ``mutate_config_files``. - - - -Making options mutable-safe ---------------------------- - -When options are mutated, they change in the ConfigOpts object but this will -not necessarily affect your service immediately. There are three main cases to -deal with: - -* The option is checked every time -* The option is cached on the stack -* The option affects state - - -The option is checked every time -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -This pattern is already safe. Example code:: - - while True: - progress_timeout = CONF.libvirt.live_migration_progress_timeout - completion_timeout = int( - CONF.libvirt.live_migration_completion_timeout * data_gb) - if libvirt_migrate.should_abort(instance, now, progress_time, - progress_timeout, completion_timeout): - guest.abort_job() - - -The option is cached on the stack -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Just putting the option value in a local variable is enough to cache it. This -is tempting to do with loops. Example code:: - - progress_timeout = CONF.libvirt.live_migration_progress_timeout - completion_timeout = int( - CONF.libvirt.live_migration_completion_timeout * data_gb) - while True: - if libvirt_migrate.should_abort(instance, now, progress_time, - progress_timeout, completion_timeout): - guest.abort_job() - -The goal is to check the option exactly once every time it could have an -effect. Usually this is as simple as checking it every time, for example by -moving the locals into the loop. Example patch: -https://review.openstack.org/#/c/319203 - -Sometimes multiple computations have to be performed using the option values -and it's important that the result is consistent. In this case, it's necessary -to cache the option values in locals. Example patch: -https://review.openstack.org/#/c/319254 - - -The option affects state -^^^^^^^^^^^^^^^^^^^^^^^^ - -An option value can also be cached, after a fashion, by state - either system -or external. For example, the 'debug' option of oslo.log is used to set the -default log level on startup. The option is not normally checked again, so if -it is mutated, the system state will not reflect the new value of the option. -In this case we have to use a *mutate hook*:: - - def _mutate_hook(conf, fresh): - if (None, 'debug') in fresh: - if conf.debug: - log_root.setLevel(logging.DEBUG) - - def register_options(conf): - ... snip ... - conf.register_mutate_hook(_mutate_hook) - -Mutate hook functions will be passed two positional parameters, 'conf' and -'fresh'. 'conf' is a reference to the updated ConfigOpts object. 'fresh' looks -like:: - - { (group, option_name): (old_value, new_value), ... } - -for example:: - - { (None, 'debug'): (False, True), - ('libvirt', 'live_migration_progress_timeout'): (50, 75) } - -Hooks may be called in any order. - -Each project should register one hook, which does whatever is necessary to -apply all the new option values. This hook function could grow very large. For -good style, modularise the hook using secondary functions rather than accreting -a monolith or registering multiple hooks. - -Example patch: https://review.openstack.org/#/c/254821/ diff --git a/doc/source/namespaces.rst b/doc/source/namespaces.rst deleted file mode 100644 index 6c5f32a..0000000 --- a/doc/source/namespaces.rst +++ /dev/null @@ -1,12 +0,0 @@ ----------------------------------------------- -Choosing group names for configuration options ----------------------------------------------- - -Applications should use a meaningful name without a prefix. For Oslo -libraries, when naming groups for configuration options using the -name of the library itself instead of a descriptive name to help avoid -collisions. If the library name is namespaced then use '_' as a separator -in the group name. - -For example, the ``oslo.log`` library should use ``oslo_log`` as the -group name. diff --git a/doc/source/opts.rst b/doc/source/opts.rst deleted file mode 100644 index e82ecff..0000000 --- a/doc/source/opts.rst +++ /dev/null @@ -1,22 +0,0 @@ ------------------- -Option Definitions ------------------- - -.. currentmodule:: oslo_config.cfg - -.. autoclass:: Opt -.. autoclass:: StrOpt -.. autoclass:: BoolOpt -.. autoclass:: IntOpt -.. autoclass:: FloatOpt -.. autoclass:: ListOpt -.. autoclass:: DictOpt -.. autoclass:: MultiOpt -.. autoclass:: MultiStrOpt -.. autoclass:: IPOpt -.. autoclass:: PortOpt -.. autoclass:: HostnameOpt -.. autoclass:: URIOpt -.. autoclass:: DeprecatedOpt -.. autoclass:: SubCommandOpt -.. autoclass:: OptGroup diff --git a/doc/source/parser.rst b/doc/source/parser.rst deleted file mode 100644 index 64b8b38..0000000 --- a/doc/source/parser.rst +++ /dev/null @@ -1,11 +0,0 @@ ------------- -File Parsing ------------- - -.. autoclass:: oslo_config.iniparser.BaseParser - -.. autoclass:: oslo_config.cfg.ConfigParser - :members: parse - -.. autoclass:: oslo_config.cfg.MultiConfigParser - :members: read, get diff --git a/doc/source/sphinxconfiggen.rst b/doc/source/sphinxconfiggen.rst deleted file mode 100644 index 5768d73..0000000 --- a/doc/source/sphinxconfiggen.rst +++ /dev/null @@ -1,50 +0,0 @@ -==================================== -Sphinx Oslo Sample Config Generation -==================================== - -Included with oslo.config is a sphinx extension to generate a sample config -file at the beginning of each sphinx build. To activate the extension add -``oslo_config.sphinxconfiggen`` to the list of extensions in your sphinx -``conf.py``. - -Then you just need to use the ``config_generator_config_file`` option to point -the config generator at the config file which tells it how to generate the -sample config. If one isn't specified or it doesn't point to a real file the -sample config file generation will be skipped. - -To generate multiple files, set ``config_generator_config_file`` to a -list of tuples containing the input filename and the base name for the -output file. - -The output value can be ``None``, in which case the name is taken from -the input value. - -The input name can be an full path or a value relative to the -documentation source directory. - -For example:: - - config_generator_config_file = [ - ('../../etc/glance-api.conf', 'api'), - ('../../etc/glance-cache.conf', 'cache'), - ('../../etc/glance-glare.conf', None), - ('../../etc/glance-registry.conf', None), - ('../../etc/glance-scrubber.conf', None), - ] - -Produces the output files ``api.conf.sample``, ``cache.conf.sample``, -``glance-glare.conf.sample``, ``glance-registry.conf.sample``, and -``glance-scrubber.conf.sample``. - -Output File Name ----------------- - -By default the sphinx plugin will generate the sample config file and -name the file ``sample.config``. However, if for whatever reason you'd -like the name to be more specific to the project name you can use the -``sample_config_basename`` config option to specify the project -name. If it's set the output filename will be that value with a -``.conf.sample`` extension. For example if you set the value to -"``nova``" the output filename will be "``nova.conf.sample``". You can -also include a subdirectory off of the documentation source directory -as part of this value. diff --git a/doc/source/sphinxext.rst b/doc/source/sphinxext.rst deleted file mode 100644 index 5e9a1a5..0000000 --- a/doc/source/sphinxext.rst +++ /dev/null @@ -1,60 +0,0 @@ -==================== - Sphinx Integration -==================== - -The ``oslo_config.sphinxext`` module defines a custom domain for -documenting configuration options. The domain includes a directive and -two roles. - -.. rst:directive:: show-options - - Given a list of namespaces, show all of the options exported from - them. - - :: - - .. show-options:: - - oslo.config - oslo.log - - To show each namespace separately, add the ``split-namespaces`` - flag. - - :: - - .. show-options:: - :split-namespaces: - - oslo.config - oslo.log - - To use an existing configuration file for the sample configuration - generator, use the ``config-file`` option instead of specifying the - namespaces inline. - :: - - .. show-options:: - :config-file: etc/oslo-config-generator/glance-api.conf - -.. rst:role:: option - - Link to an option. - - :: - - #. :oslo.config:option:`config_file` - #. :oslo.config:option:`DEFAULT.config_file` - - #. :oslo.config:option:`config_file` - #. :oslo.config:option:`DEFAULT.config_file` - -.. rst:role:: group - - Link to an option group. - - :: - - :oslo.config:group:`DEFAULT` - - :oslo.config:group:`DEFAULT` diff --git a/doc/source/styleguide.rst b/doc/source/styleguide.rst deleted file mode 100644 index 73ff5b8..0000000 --- a/doc/source/styleguide.rst +++ /dev/null @@ -1,84 +0,0 @@ ----------------------------- -Style Guide for Help Strings ----------------------------- - -This document provides style guidance for writing the required help -strings for configuration options using the ``oslo.config`` code. - -The help strings are parsed from the code to appear in sample -configuration files, such as ``etc/cinder/cinder.conf`` in the -cinder repository. They are also displayed in the `OpenStack -Configuration Reference -`_. - -Examples:: - - cfg.StrOpt('bind_host', - default='0.0.0.0', - help='IP address to listen on.'), - cfg.PortOpt('bind_port', - default=9292, - help='Port number to listen on.') - - -Style Guide ------------ - -1. Use sentence-style capitalization for help strings: Capitalize or - uppercase the first character (see the examples above). - -2. Only use single spaces, no double spaces. - -3. Properly capitalize words. If in doubt check the `OpenStack Glossary `_. - -4. End each segment with a period and write complete sentences if - possible. Examples:: - - cfg.StrOpt('osapi_volume_base_URL', - default=None, - help='Base URL that appears in links to the OpenStack ' - 'Block Storage API.') - - - cfg.StrOpt('host', - default=socket.gethostname(), - help='Name of this node. This can be an opaque identifier. ' - 'It is not necessarily a host name, FQDN, or IP address.') - -5. Use valid service names and API names. Valid service names include - nova, cinder, swift, glance, heat, neutron, trove, ceilometer, - horizon, keystone, and marconi. - - Valid API names include Compute API, Image Service API, Identity - Service API, Object Storage API, Block Storage API, Database API, - and Networking API. - -Format ------- - -1. For multi-line strings, remember that strings are concatenated - directly and thus spaces need to be inserted normally. - - This document recommends to add the space at the end of a line and - not at the beginning. Example:: - - cfg.BoolOpt('glance_api_ssl_compression', - default=False, - help='Enables or disables negotiation of SSL layer ' - 'compression. In some cases disabling compression ' - 'can improve data throughput, such as when high ' - 'network bandwidth is available and you use ' - 'compressed image formats like qcow2.') - -2. It is possible to preformat the multi-line strings to increase readability. - Line break characters ``\n`` will be kept as they are used in the help text. - Example:: - - cfg.IntOpt('sync_power_state_interval', - default=600, - help='Interval to sync power states between the database and ' - 'the hypervisor.\n' - '\n' - '-1: disables the sync \n' - ' 0: run at the default rate.\n' - '>0: the interval in seconds') diff --git a/doc/source/types.rst b/doc/source/types.rst deleted file mode 100644 index 9f7ea13..0000000 --- a/doc/source/types.rst +++ /dev/null @@ -1,6 +0,0 @@ ---------------------------- -Option Types and Validation ---------------------------- - -.. automodule:: oslo_config.types - :members: diff --git a/oslo_config/__init__.py b/oslo_config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/oslo_config/_i18n.py b/oslo_config/_i18n.py deleted file mode 100644 index 21f8c86..0000000 --- a/oslo_config/_i18n.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2016 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""oslo.i18n integration module. - -See http://docs.openstack.org/developer/oslo.i18n/usage.html . - -""" - -import oslo_i18n - -DOMAIN = "oslo.config" - -_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) - -# The primary translation function using the well-known name "_" -_ = _translators.primary - -# The contextual translation function using the name "_C" -# requires oslo.i18n >=2.1.0 -_C = _translators.contextual_form - -# The plural translation function using the name "_P" -# requires oslo.i18n >=2.1.0 -_P = _translators.plural_form - -# Translators for log levels. -# -# The abbreviated names are meant to reflect the usual use of a short -# name like '_'. The "L" is for "log" and the other letter comes from -# the level. -_LI = _translators.log_info -_LW = _translators.log_warning -_LE = _translators.log_error -_LC = _translators.log_critical - - -def get_available_languages(): - return oslo_i18n.get_available_languages(DOMAIN) diff --git a/oslo_config/_list_opts.py b/oslo_config/_list_opts.py deleted file mode 100644 index 0b6b27f..0000000 --- a/oslo_config/_list_opts.py +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - - -def list_opts(): - default_config_files = [ - '~/.project/project.conf', - '~/project.conf', - '/etc/project/project.conf', - '/etc/project.conf', - ] - return [ - (None, cfg.ConfigOpts._make_config_options(default_config_files)), - ] diff --git a/oslo_config/cfg.py b/oslo_config/cfg.py deleted file mode 100644 index 68cb8c2..0000000 --- a/oslo_config/cfg.py +++ /dev/null @@ -1,3147 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -r""" -Configuration options may be set on the command line or in config files. - -The schema for each option is defined using the -:class:`Opt` class or its sub-classes, for example: - -:: - - from oslo_config import cfg - from oslo_config import types - - PortType = types.Integer(1, 65535) - - common_opts = [ - cfg.StrOpt('bind_host', - default='0.0.0.0', - help='IP address to listen on.'), - cfg.Opt('bind_port', - type=PortType, - default=9292, - help='Port number to listen on.') - ] - -Option Types ------------- - -Options can have arbitrary types via the `type` parameter to the :class:`Opt` -constructor. The `type` parameter is a callable object that takes a string and -either returns a value of that particular type or raises :class:`ValueError` if -the value can not be converted. - -For convenience, there are predefined option subclasses in -:mod:`oslo_config.cfg` that set the option `type` as in the following table: - -==================================== ====== -Type Option -==================================== ====== -:class:`oslo_config.types.String` - :class:`oslo_config.cfg.StrOpt` - - :class:`oslo_config.cfg.SubCommandOpt` -:class:`oslo_config.types.Boolean` :class:`oslo_config.cfg.BoolOpt` -:class:`oslo_config.types.Integer` - :class:`oslo_config.cfg.IntOpt` - - :class:`oslo_config.cfg.PortOpt` -:class:`oslo_config.types.Float` :class:`oslo_config.cfg.FloatOpt` -:class:`oslo_config.types.List` :class:`oslo_config.cfg.ListOpt` -:class:`oslo_config.types.Dict` :class:`oslo_config.cfg.DictOpt` -:class:`oslo_config.types.IPAddress` :class:`oslo_config.cfg.IPOpt` -:class:`oslo_config.types.Hostname` :class:`oslo_config.cfg.HostnameOpt` -:class:`oslo_config.types.URI` :class:`oslo_config.cfg.URIOpt` -==================================== ====== - -For :class:`oslo_config.cfg.MultiOpt` the `item_type` parameter defines -the type of the values. For convenience, :class:`oslo_config.cfg.MultiStrOpt` -is :class:`~oslo_config.cfg.MultiOpt` with the `item_type` parameter set to -:class:`oslo_config.types.MultiString`. - -The following example defines options using the convenience classes:: - - enabled_apis_opt = cfg.ListOpt('enabled_apis', - default=['ec2', 'osapi_compute'], - help='List of APIs to enable by default.') - - DEFAULT_EXTENSIONS = [ - 'nova.api.openstack.compute.contrib.standard_extensions' - ] - osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension', - default=DEFAULT_EXTENSIONS) - -Registering Options -------------------- - -Option schemas are registered with the config manager at runtime, but before -the option is referenced:: - - class ExtensionManager(object): - - enabled_apis_opt = cfg.ListOpt(...) - - def __init__(self, conf): - self.conf = conf - self.conf.register_opt(enabled_apis_opt) - ... - - def _load_extensions(self): - for ext_factory in self.conf.osapi_compute_extension: - .... - -A common usage pattern is for each option schema to be defined in the module or -class which uses the option:: - - opts = ... - - def add_common_opts(conf): - conf.register_opts(opts) - - def get_bind_host(conf): - return conf.bind_host - - def get_bind_port(conf): - return conf.bind_port - -An option may optionally be made available via the command line. Such options -must be registered with the config manager before the command line is parsed -(for the purposes of --help and CLI arg validation):: - - cli_opts = [ - cfg.BoolOpt('verbose', - short='v', - default=False, - help='Print more verbose output.'), - cfg.BoolOpt('debug', - short='d', - default=False, - help='Print debugging output.'), - ] - - def add_common_opts(conf): - conf.register_cli_opts(cli_opts) - -Loading Config Files --------------------- - -The config manager has two CLI options defined by default, --config-file -and --config-dir:: - - class ConfigOpts(object): - - def __call__(self, ...): - - opts = [ - MultiStrOpt('config-file', - ...), - StrOpt('config-dir', - ...), - ] - - self.register_cli_opts(opts) - -Option values are parsed from any supplied config files using -oslo_config.iniparser. If none are specified, a default set is used -for example glance-api.conf and glance-common.conf:: - - glance-api.conf: - [DEFAULT] - bind_port = 9292 - - glance-common.conf: - [DEFAULT] - bind_host = 0.0.0.0 - -Option values in config files and those on the command line are parsed -in order. The same option (includes deprecated option name and current -option name) can appear many times, in config files or on the command line. -Later values always override earlier ones. - -The order of configuration files inside the same configuration directory is -defined by the alphabetic sorting order of their file names. - -The parsing of CLI args and config files is initiated by invoking the config -manager for example:: - - conf = cfg.ConfigOpts() - conf.register_opt(cfg.BoolOpt('verbose', ...)) - conf(sys.argv[1:]) - if conf.verbose: - ... - -Option Groups -------------- - -Options can be registered as belonging to a group:: - - rabbit_group = cfg.OptGroup(name='rabbit', - title='RabbitMQ options') - - rabbit_host_opt = cfg.StrOpt('host', - default='localhost', - help='IP/hostname 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) - # options can be registered under a group in either of these ways: - conf.register_opt(rabbit_host_opt, group=rabbit_group) - conf.register_opt(rabbit_port_opt, group='rabbit') - -If no group attributes are required other than the group name, the group -need not be explicitly registered for example:: - - def register_rabbit_opts(conf): - # The group will automatically be created, equivalent calling:: - # conf.register_group(OptGroup(name='rabbit')) - conf.register_opt(rabbit_port_opt, group='rabbit') - -If no group is specified, options belong to the 'DEFAULT' section of config -files:: - - glance-api.conf: - [DEFAULT] - bind_port = 9292 - ... - - [rabbit] - host = localhost - port = 5672 - use_ssl = False - userid = guest - password = guest - virtual_host = / - -Command-line options in a group are automatically prefixed with the -group name:: - - --rabbit-host localhost --rabbit-port 9999 - -Accessing Option Values In Your Code ------------------------------------- - -Option values in the default group are referenced as attributes/properties on -the config manager; groups are also attributes on the config manager, with -attributes for each of the options associated with the group:: - - server.start(app, conf.bind_port, conf.bind_host, conf) - - self.connection = kombu.connection.BrokerConnection( - hostname=conf.rabbit.host, - port=conf.rabbit.port, - ...) - -Option Value Interpolation --------------------------- - -Option values may reference other values using PEP 292 string substitution:: - - opts = [ - cfg.StrOpt('state_path', - default=os.path.join(os.path.dirname(__file__), '../'), - help='Top-level directory for maintaining nova state.'), - cfg.StrOpt('sqlite_db', - default='nova.sqlite', - help='File name for SQLite.'), - cfg.StrOpt('sql_connection', - default='sqlite:///$state_path/$sqlite_db', - help='Connection string for SQL database.'), - ] - -.. note:: - - Interpolation can be avoided by using `$$`. - -.. note:: - - You can use `.` to delimit option from other groups, e.g. - ${mygroup.myoption}. - -Special Handling Instructions ------------------------------ - -Options may be declared as required so that an error is raised if the user -does not supply a value for the option:: - - opts = [ - cfg.StrOpt('service_name', required=True), - cfg.StrOpt('image_id', required=True), - ... - ] - -Options may be declared as secret so that their values are not leaked into -log files:: - - opts = [ - cfg.StrOpt('s3_store_access_key', secret=True), - cfg.StrOpt('s3_store_secret_key', secret=True), - ... - ] - -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 ------------------ - -This module also contains a global instance of the ConfigOpts class -in order to support a common usage pattern in OpenStack:: - - from oslo_config import cfg - - opts = [ - cfg.StrOpt('bind_host', default='0.0.0.0'), - cfg.PortOpt('bind_port', default=9292), - ] - - CONF = cfg.CONF - CONF.register_opts(opts) - - def start(server, app): - server.start(app, CONF.bind_port, CONF.bind_host) - -Positional Command Line Arguments ---------------------------------- - -Positional command line arguments are supported via a 'positional' Opt -constructor argument:: - - >>> conf = cfg.ConfigOpts() - >>> conf.register_cli_opt(cfg.MultiStrOpt('bar', positional=True)) - True - >>> conf(['a', 'b']) - >>> conf.bar - ['a', 'b'] - -Sub-Parsers ------------ - -It is also possible to use argparse "sub-parsers" to parse additional -command line arguments using the SubCommandOpt class: - - >>> def add_parsers(subparsers): - ... list_action = subparsers.add_parser('list') - ... list_action.add_argument('id') - ... - >>> conf = cfg.ConfigOpts() - >>> conf.register_cli_opt(cfg.SubCommandOpt('action', handler=add_parsers)) - True - >>> conf(args=['list', '10']) - >>> conf.action.name, conf.action.id - ('list', '10') - -Advanced Option ---------------- - -Use if you need to label an option as advanced in sample files, indicating the -option is not normally used by the majority of users and might have a -significant effect on stability and/or performance:: - - from oslo_config import cfg - - opts = [ - cfg.StrOpt('option1', default='default_value', - advanced=True, help='This is help ' - 'text.'), - cfg.PortOpt('option2', default='default_value', - help='This is help text.'), - ] - - CONF = cfg.CONF - CONF.register_opts(opts) - -This will result in the option being pushed to the bottom of the -namespace and labeled as advanced in the sample files, with a notation -about possible effects:: - - [DEFAULT] - ... - # This is help text. (string value) - # option2 = default_value - ... - - ... - # This is help text. (string value) - # Advanced Option: intended for advanced users and not used - # by the majority of users, and might have a significant - # effect on stability and/or performance. - # option1 = default_value - -""" - -import argparse -import collections -import copy -import errno -import functools -import glob -import itertools -import logging -import os -import string -import sys - -from debtcollector import removals -import six -from six import moves - -from oslo_config._i18n import _LI, _LW -from oslo_config import iniparser -from oslo_config import types - -LOG = logging.getLogger(__name__) - - -class Error(Exception): - """Base class for cfg exceptions.""" - - def __init__(self, msg=None): - self.msg = msg - - def __str__(self): - return self.msg - - -class NotInitializedError(Error): - """Raised if parser is not initialized yet.""" - - def __str__(self): - return "call expression on parser has not been invoked" - - -class ArgsAlreadyParsedError(Error): - """Raised if a CLI opt is registered after parsing.""" - - def __str__(self): - ret = "arguments already parsed" - if self.msg: - ret += ": " + self.msg - return ret - - -class NoSuchOptError(Error, AttributeError): - """Raised if an opt which doesn't exist is referenced.""" - - def __init__(self, opt_name, group=None): - self.opt_name = opt_name - self.group = group - - def __str__(self): - group_name = 'DEFAULT' if self.group is None else self.group.name - return "no such option %s in group [%s]" % (self.opt_name, group_name) - - -class NoSuchGroupError(Error): - """Raised if a group which doesn't exist is referenced.""" - - def __init__(self, group_name): - self.group_name = group_name - - def __str__(self): - return "no such group [%s]" % self.group_name - - -class DuplicateOptError(Error): - """Raised if multiple opts with the same name are registered.""" - - def __init__(self, opt_name): - self.opt_name = opt_name - - def __str__(self): - return "duplicate option: %s" % self.opt_name - - -class RequiredOptError(Error): - """Raised if an option is required but no value is supplied by the user.""" - - def __init__(self, opt_name, group=None): - self.opt_name = opt_name - self.group = group - - def __str__(self): - group_name = 'DEFAULT' if self.group is None else self.group.name - return "value required for option %s in group [%s]" % (self.opt_name, - group_name) - - -class TemplateSubstitutionError(Error): - """Raised if an error occurs substituting a variable in an opt value.""" - - def __str__(self): - return "template substitution error: %s" % self.msg - - -class ConfigFilesNotFoundError(Error): - """Raised if one or more config files are not found.""" - - def __init__(self, config_files): - self.config_files = config_files - - def __str__(self): - return ('Failed to find some config files: %s' % - ",".join(self.config_files)) - - -class ConfigFilesPermissionDeniedError(Error): - """Raised if one or more config files are not readable.""" - - def __init__(self, config_files): - self.config_files = config_files - - def __str__(self): - return ('Failed to open some config files: %s' % - ",".join(self.config_files)) - - -class ConfigDirNotFoundError(Error): - """Raised if the requested config-dir is not found.""" - - def __init__(self, config_dir): - self.config_dir = config_dir - - def __str__(self): - return ('Failed to read config file directory: %s' % self.config_dir) - - -class ConfigFileParseError(Error): - """Raised if there is an error parsing a config file.""" - - def __init__(self, config_file, msg): - self.config_file = config_file - self.msg = msg - - def __str__(self): - return 'Failed to parse %s: %s' % (self.config_file, self.msg) - - -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)) - - -def _get_config_dirs(project=None): - """Return a list of directories where config files may be located. - - :param project: an optional project name - - If a project is specified, following directories are returned:: - - ~/.${project}/ - ~/ - /etc/${project}/ - /etc/ - - Otherwise, these directories:: - - ~/ - /etc/ - """ - cfg_dirs = [ - _fixpath(os.path.join('~', '.' + project)) if project else None, - _fixpath('~'), - os.path.join('/etc', project) if project else None, - '/etc' - ] - - return list(moves.filter(bool, cfg_dirs)) - - -def _search_dirs(dirs, basename, extension=""): - """Search a list of directories for a given filename. - - Iterator over the supplied directories, returning the first file - found with the supplied name and extension. - - :param dirs: a list of directories - :param basename: the filename, for example 'glance-api' - :param extension: the file extension, for example '.conf' - :returns: the path to a matching file, or None - """ - for d in dirs: - path = os.path.join(d, '%s%s' % (basename, extension)) - if os.path.exists(path): - return path - - -def find_config_files(project=None, prog=None, extension='.conf'): - """Return a list of default configuration files. - - :param project: an optional project name - :param prog: the program name, defaulting to the basename of sys.argv[0] - :param extension: the type of the config file - - We default to two config files: [${project}.conf, ${prog}.conf] - - And we look for those config files in the following directories:: - - ~/.${project}/ - ~/ - /etc/${project}/ - /etc/ - - We return an absolute path for (at most) one of each the default config - files, for the topmost directory it exists in. - - For example, if project=foo, prog=bar and /etc/foo/foo.conf, /etc/bar.conf - and ~/.foo/bar.conf all exist, then we return ['/etc/foo/foo.conf', - '~/.foo/bar.conf'] - - If no project name is supplied, we only look for ${prog.conf}. - """ - if prog is None: - prog = os.path.basename(sys.argv[0]) - - cfg_dirs = _get_config_dirs(project) - - config_files = [] - if project: - config_files.append(_search_dirs(cfg_dirs, project, extension)) - config_files.append(_search_dirs(cfg_dirs, prog, extension)) - - return list(moves.filter(bool, config_files)) - - -def _is_opt_registered(opts, opt): - """Check whether an opt with the same name is already registered. - - The same opt may be registered multiple times, with only the first - registration having any effect. However, it is an error to attempt - to register a different opt with the same name. - - :param opts: the set of opts already registered - :param opt: the opt to be registered - :returns: True if the opt was previously registered, False otherwise - :raises: DuplicateOptError if a naming conflict is detected - """ - if opt.dest in opts: - if opts[opt.dest]['opt'] != opt: - raise DuplicateOptError(opt.name) - return True - else: - return False - - -def set_defaults(opts, **kwargs): - for opt in opts: - if opt.dest in kwargs: - opt.default = kwargs[opt.dest] - - -def _normalize_group_name(group_name): - if group_name == 'DEFAULT': - return group_name - return group_name.lower() - - -@functools.total_ordering -class Opt(object): - - """Base class for all configuration options. - - The only required parameter is the option's name. However, it is - common to also supply a default and help string for all options. - - :param name: the option's name - :param type: the option's type. Must be a callable object that takes string - and returns converted and validated value - :param dest: the name of the corresponding :class:`.ConfigOpts` property - :param short: a single character CLI option name - :param default: the default value of the option - :param positional: True if the option is a positional CLI argument - :param metavar: the option argument to show in --help - :param help: an explanation of how the option is used - :param secret: true if the value should be obfuscated in log output - :param required: true if a value must be supplied for this option - :param deprecated_name: deprecated name option. Acts like an alias - :param deprecated_group: the group containing a deprecated alias - :param deprecated_opts: list of :class:`.DeprecatedOpt` - :param sample_default: a default string for sample config files - :param deprecated_for_removal: indicates whether this opt is planned for - removal in a future release - :param deprecated_reason: indicates why this opt is planned for removal in - a future release. Silently ignored if - deprecated_for_removal is False - :param deprecated_since: indicates which release this opt was deprecated - in. Accepts any string, though valid version - strings are encouraged. Silently ignored if - deprecated_for_removal is False - :param mutable: True if this option may be reloaded - :param advanced: a bool True/False value if this option has advanced usage - and is not normally used by the majority of users - - An Opt object has no public methods, but has a number of public properties: - - .. py:attribute:: name - - the name of the option, which may include hyphens - - .. py:attribute:: type - - a callable object that takes string and returns converted and - validated value. Default types are available from - :class:`oslo_config.types` - - .. py:attribute:: dest - - the (hyphen-less) :class:`.ConfigOpts` property which contains the - option value - - .. py:attribute:: short - - a single character CLI option name - - .. py:attribute:: default - - the default value of the option - - .. py:attribute:: sample_default - - a sample default value string to include in sample config files - - .. py:attribute:: positional - - True if the option is a positional CLI argument - - .. py:attribute:: metavar - - the name shown as the argument to a CLI option in --help output - - .. py:attribute:: help - - a string explaining how the option's value is used - - .. py:attribute:: advanced - - in sample files, a bool value indicating the option is advanced - - .. 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. - - .. versionchanged:: 3.2 - Added *deprecated_reason* parameter. - - .. versionchanged:: 3.5 - Added *mutable* parameter. - - .. versionchanged:: 3.12 - Added *deprecated_since* parameter. - - .. versionchanged:: 3.15 - Added *advanced* parameter and attribute. - """ - multi = False - - def __init__(self, name, type=None, dest=None, short=None, - default=None, positional=False, metavar=None, help=None, - secret=False, required=False, - deprecated_name=None, deprecated_group=None, - deprecated_opts=None, sample_default=None, - deprecated_for_removal=False, deprecated_reason=None, - deprecated_since=None, mutable=False, advanced=False): - if name.startswith('_'): - raise ValueError('illegal name %s with prefix _' % (name,)) - self.name = name - - if type is None: - type = types.String() - - if not callable(type): - raise TypeError('type must be callable') - self.type = type - - if dest is None: - self.dest = self.name.replace('-', '_') - else: - self.dest = dest - self.short = short - self.default = default - self.sample_default = sample_default - self.positional = positional - self.metavar = metavar - self.help = help - self.secret = secret - self.required = required - self.deprecated_for_removal = deprecated_for_removal - self.deprecated_reason = deprecated_reason - self.deprecated_since = deprecated_since - self._logged_deprecation = False - if deprecated_name is not None: - deprecated_name = deprecated_name.replace('-', '_') - - self.deprecated_opts = copy.deepcopy(deprecated_opts) or [] - if deprecated_name is not None or deprecated_group is not None: - self.deprecated_opts.append(DeprecatedOpt(deprecated_name, - group=deprecated_group)) - self._check_default() - - self.mutable = mutable - self.advanced = advanced - - def _default_is_ref(self): - """Check if default is a reference to another var.""" - if isinstance(self.default, six.string_types): - tmpl = self.default.replace('\$', '').replace('$$', '') - return '$' in tmpl - return False - - def _check_default(self): - if (self.default is not None - 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) - - def __eq__(self, another): - return vars(self) == vars(another) - - __hash__ = object.__hash__ - - def _get_from_namespace(self, namespace, group_name): - """Retrieves the option value from a _Namespace object. - - :param namespace: a _Namespace object - :param group_name: a group name - """ - names = [(group_name, self.dest)] - current_name = (group_name, self.name) - - for opt in self.deprecated_opts: - dname, dgroup = opt.name, opt.group - if dname or dgroup: - names.append((dgroup if dgroup else group_name, - dname if dname else self.dest)) - - value = namespace._get_value( - names, multi=self.multi, - positional=self.positional, current_name=current_name) - # The previous line will raise a KeyError if no value is set in the - # config file, so we'll only log deprecations for set options. - if self.deprecated_for_removal and not self._logged_deprecation: - self._logged_deprecation = True - pretty_group = group_name or 'DEFAULT' - LOG.warning(_LW('Option "%(option)s" from group "%(group)s" is ' - 'deprecated for removal. Its value may be ' - 'silently ignored in the future.'), - {'option': self.dest, 'group': pretty_group}) - return value - - def _add_to_cli(self, parser, group=None): - """Makes the option available in the command line interface. - - This is the method ConfigOpts uses to add the opt to the CLI interface - as appropriate for the opt type. Some opt types may extend this method, - others may just extend the helper methods it uses. - - :param parser: the CLI option parser - :param group: an optional OptGroup object - """ - container = self._get_argparse_container(parser, group) - kwargs = self._get_argparse_kwargs(group) - prefix = self._get_argparse_prefix('', group.name if group else None) - deprecated_names = [] - for opt in self.deprecated_opts: - deprecated_name = self._get_deprecated_cli_name(opt.name, - opt.group) - if deprecated_name is not None: - deprecated_names.append(deprecated_name) - self._add_to_argparse(parser, container, self.name, self.short, - kwargs, prefix, - self.positional, deprecated_names) - - def _add_to_argparse(self, parser, container, name, short, kwargs, - prefix='', positional=False, deprecated_names=None): - """Add an option to an argparse parser or group. - - :param container: an argparse._ArgumentGroup object - :param name: the opt name - :param short: the short opt name - :param kwargs: the keyword arguments for add_argument() - :param prefix: an optional prefix to prepend to the opt name - :param positional: whether the option is a positional CLI argument - :param deprecated_names: list of deprecated option names - """ - def hyphen(arg): - return arg if not positional else '' - - args = [hyphen('--') + prefix + name] - if short: - args.append(hyphen('-') + short) - for deprecated_name in deprecated_names: - args.append(hyphen('--') + deprecated_name) - - parser.add_parser_argument(container, *args, **kwargs) - - def _get_argparse_container(self, parser, group): - """Returns an argparse._ArgumentGroup. - - :param parser: an argparse.ArgumentParser - :param group: an (optional) OptGroup object - :returns: an argparse._ArgumentGroup if group is given, else parser - """ - if group is not None: - return group._get_argparse_group(parser) - else: - return parser - - def _get_argparse_kwargs(self, group, **kwargs): - """Build a dict of keyword arguments for argparse's add_argument(). - - Most opt types extend this method to customize the behaviour of the - options added to argparse. - - :param group: an optional group - :param \*\*kwargs: optional keyword arguments to add to - :returns: a dict of keyword arguments - """ - if not self.positional: - dest = self.dest - if group is not None: - dest = group.name + '_' + dest - kwargs['dest'] = dest - else: - kwargs['nargs'] = '?' - kwargs.update({'default': None, - 'metavar': self.metavar, - 'help': self.help, }) - return kwargs - - def _get_argparse_prefix(self, prefix, group_name): - """Build a prefix for the CLI option name, if required. - - CLI options in a group are prefixed with the group's name in order - to avoid conflicts between similarly named options in different - groups. - - :param prefix: an existing prefix to append to (for example 'no' or '') - :param group_name: an optional group name - :returns: a CLI option prefix including the group name, if appropriate - """ - if group_name is not None: - return group_name + '-' + prefix - else: - return prefix - - def _get_deprecated_cli_name(self, dname, dgroup, prefix=''): - """Build a CLi arg name for deprecated options. - - Either a deprecated name or a deprecated group or both or - neither can be supplied: - - dname, dgroup -> dgroup + '-' + dname - dname -> dname - dgroup -> dgroup + '-' + self.name - neither -> None - - :param dname: a deprecated name, which can be None - :param dgroup: a deprecated group, which can be None - :param prefix: an prefix to append to (for example 'no' or '') - :returns: a CLI argument name - """ - if dgroup == 'DEFAULT': - dgroup = None - - if dname is None and dgroup is None: - return None - - if dname is None: - dname = self.name - - return self._get_argparse_prefix(prefix, dgroup) + dname - - def __lt__(self, another): - return hash(self) < hash(another) - - -class DeprecatedOpt(object): - - """Represents a Deprecated option. - - Here's how you can use it:: - - oldopts = [cfg.DeprecatedOpt('oldopt1', group='group1'), - cfg.DeprecatedOpt('oldopt2', group='group2')] - cfg.CONF.register_group(cfg.OptGroup('group1')) - cfg.CONF.register_opt(cfg.StrOpt('newopt', deprecated_opts=oldopts), - group='group1') - - For options which have a single value (like in the example above), - if the new option is present ("[group1]/newopt" above), it will override - any deprecated options present ("[group1]/oldopt1" and "[group2]/oldopt2" - above). - - If no group is specified for a DeprecatedOpt option (i.e. the group is - None), lookup will happen within the same group the new option is in. - For example, if no group was specified for the second option 'oldopt2' in - oldopts list:: - - oldopts = [cfg.DeprecatedOpt('oldopt1', group='group1'), - cfg.DeprecatedOpt('oldopt2')] - cfg.CONF.register_group(cfg.OptGroup('group1')) - cfg.CONF.register_opt(cfg.StrOpt('newopt', deprecated_opts=oldopts), - group='group1') - - then lookup for that option will happen in group 'group1'. - - If the new option is not present and multiple deprecated options are - present, the option corresponding to the first element of deprecated_opts - will be chosen. - - Multi-value options will return all new and deprecated - options. So if we have a multi-value option "[group1]/opt1" whose - deprecated option is "[group2]/opt2", and the conf file has both these - options specified like so:: - - [group1] - opt1=val10,val11 - - [group2] - opt2=val21,val22 - - Then the value of "[group1]/opt1" will be ['val11', 'val12', 'val21', - 'val22']. - - .. versionadded:: 1.2 - """ - - def __init__(self, name, group=None): - """Constructs an DeprecatedOpt object. - - :param name: the name of the option - :param group: the group of the option - """ - self.name = name - self.group = group - - def __key(self): - return (self.name, self.group) - - def __eq__(self, other): - return self.__key() == other.__key() - - def __hash__(self): - return hash(self.__key()) - - -class StrOpt(Opt): - """Option with String type - - Option with ``type`` :class:`oslo_config.types.String` - - :param name: the option's name - :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. - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - .. 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, quotes=None, - regex=None, ignore_case=None, max_length=None, **kwargs): - super(StrOpt, self).__init__(name, - type=types.String( - choices=choices, - quotes=quotes, - regex=regex, - ignore_case=ignore_case, - max_length=max_length), - **kwargs) - - -class BoolOpt(Opt): - - """Boolean options. - - Bool opts are set to True or False on the command line using --optname or - --nooptname respectively. - - In config files, boolean values are cast with Boolean type. - - :param name: the option's name - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - """ - - def __init__(self, name, **kwargs): - if 'positional' in kwargs: - raise ValueError('positional boolean args not supported') - super(BoolOpt, self).__init__(name, type=types.Boolean(), **kwargs) - - def _add_to_cli(self, parser, group=None): - """Extends the base class method to add the --nooptname option.""" - super(BoolOpt, self)._add_to_cli(parser, group) - self._add_inverse_to_argparse(parser, group) - - def _add_inverse_to_argparse(self, parser, group): - """Add the --nooptname option to the option parser.""" - container = self._get_argparse_container(parser, group) - kwargs = self._get_argparse_kwargs(group, action='store_false') - prefix = self._get_argparse_prefix('no', group.name if group else None) - deprecated_names = [] - for opt in self.deprecated_opts: - deprecated_name = self._get_deprecated_cli_name(opt.name, - opt.group, - prefix='no') - if deprecated_name is not None: - deprecated_names.append(deprecated_name) - kwargs["help"] = "The inverse of --" + self.name - self._add_to_argparse(parser, container, self.name, None, kwargs, - prefix, self.positional, deprecated_names) - - def _get_argparse_kwargs(self, group, action='store_true', **kwargs): - """Extends the base argparse keyword dict for boolean options.""" - - kwargs = super(BoolOpt, self)._get_argparse_kwargs(group, **kwargs) - # type has no effect for BoolOpt, it only matters for - # values that came from config files - if 'type' in kwargs: - del kwargs['type'] - - # metavar has no effect for BoolOpt - if 'metavar' in kwargs: - del kwargs['metavar'] - - kwargs['action'] = action - - return kwargs - - -class IntOpt(Opt): - - """Option with Integer type - - Option with ``type`` :class:`oslo_config.types.Integer` - - :param name: the option's name - :param min: minimum value the integer can take - :param max: maximum value the integer can take - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - .. versionchanged:: 1.15 - - Added *min* and *max* parameters. - """ - - def __init__(self, name, min=None, max=None, **kwargs): - super(IntOpt, self).__init__(name, type=types.Integer(min, max), - **kwargs) - - -class FloatOpt(Opt): - - """Option with Float type - - Option with ``type`` :class:`oslo_config.types.Float` - :param min: minimum value the float can take - :param max: maximum value the float can take - - :param name: the option's name - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - .. versionchanged:: 3.14 - - Added *min* and *max* parameters. - """ - - def __init__(self, name, min=None, max=None, **kwargs): - super(FloatOpt, self).__init__(name, type=types.Float(min, max), - **kwargs) - - -class ListOpt(Opt): - - """Option with List(String) type - - Option with ``type`` :class:`oslo_config.types.List` - - :param name: the option's name - :param item_type: type of items (see :class:`oslo_config.types`) - :param bounds: if True the value should be inside "[" and "]" pair - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - .. versionchanged:: 2.5 - Added *item_type* and *bounds* parameters. - """ - - 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): - - """Option with Dict(String) type - - Option with ``type`` :class:`oslo_config.types.Dict` - - :param name: the option's name - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - .. versionadded:: 1.2 - """ - - def __init__(self, name, **kwargs): - super(DictOpt, self).__init__(name, type=types.Dict(), **kwargs) - - -class IPOpt(Opt): - - """Opt with IPAddress type - - Option with ``type`` :class:`oslo_config.types.IPAddress` - - :param name: the option's name - :param version: one of either ``4``, ``6``, or ``None`` to specify - either version. - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - .. versionadded:: 1.4 - """ - - def __init__(self, name, version=None, **kwargs): - super(IPOpt, self).__init__(name, type=types.IPAddress(version), - **kwargs) - - -class PortOpt(Opt): - - """Option for a TCP/IP port number. Ports can range from 0 to 65535. - - Option with ``type`` :class:`oslo_config.types.Integer` - - :param name: the option's name - :param choices: Optional sequence of valid values. - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - :param min: minimum value the port can take - :param max: maximum value the port can take - - .. versionadded:: 2.6 - .. versionchanged:: 3.2 - Added *choices* parameter. - .. versionchanged:: 3.4 - Allow port number with 0. - .. versionchanged:: 3.16 - Added *min* and *max* parameters. - """ - - def __init__(self, name, min=None, max=None, choices=None, **kwargs): - type = types.Port(min=min, max=max, choices=choices, - type_name='port value') - super(PortOpt, self).__init__(name, type=type, **kwargs) - - -class HostnameOpt(Opt): - - """Option for a hostname. Only accepts valid hostnames. - - Option with ``type`` :class:`oslo_config.types.Hostname` - - .. versionadded:: 3.8 - """ - - def __init__(self, name, **kwargs): - super(HostnameOpt, self).__init__(name, type=types.Hostname(), - **kwargs) - - -class URIOpt(Opt): - - """Opt with URI type - - Option with ``type`` :class:`oslo_config.types.URI` - - :param max_length: If positive integer, the value must be less than or - equal to this parameter. - - .. versionadded:: 3.12 - - .. versionchanged:: 3.14 - Added *max_length* parameter - """ - - def __init__(self, name, max_length=None, **kwargs): - super(URIOpt, self).__init__(name, - type=types.URI(max_length=max_length), - **kwargs) - - -class MultiOpt(Opt): - - """Multi-value option. - - Multi opt values are typed opts which may be specified multiple times. - The opt value is a list containing all the values specified. - - :param name: the option's name - :param item_type: Type of items (see :class:`oslo_config.types`) - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`Opt` - - For example:: - - cfg.MultiOpt('foo', - item_type=types.Integer(), - default=None, - help="Multiple foo option") - - The command line ``--foo=1 --foo=2`` would result in ``cfg.CONF.foo`` - containing ``[1,2]`` - - .. versionadded:: 1.3 - """ - multi = True - - def __init__(self, name, item_type, **kwargs): - super(MultiOpt, self).__init__(name, item_type, **kwargs) - - def _get_argparse_kwargs(self, group, **kwargs): - """Extends the base argparse keyword dict for multi value options.""" - kwargs = super(MultiOpt, self)._get_argparse_kwargs(group) - if not self.positional: - kwargs['action'] = 'append' - else: - kwargs['nargs'] = '*' - return kwargs - - -class MultiStrOpt(MultiOpt): - - """MultiOpt with a MultiString ``item_type``. - - MultiOpt with a default :class:`oslo_config.types.MultiString` item - type. - - :param name: the option's name - :param \*\*kwargs: arbitrary keyword arguments passed to :class:`MultiOpt` - """ - - def __init__(self, name, **kwargs): - super(MultiStrOpt, self).__init__(name, - item_type=types.MultiString(), - **kwargs) - - -class SubCommandOpt(Opt): - - """Sub-command options. - - Sub-command options allow argparse sub-parsers to be used to parse - additional command line arguments. - - The handler argument to the SubCommandOpt constructor is a callable - which is supplied an argparse subparsers object. Use this handler - callable to add sub-parsers. - - The opt value is SubCommandAttr object with the name of the chosen - sub-parser stored in the 'name' attribute and the values of other - sub-parser arguments available as additional attributes. - - :param name: the option's name - :param dest: the name of the corresponding :class:`.ConfigOpts` property - :param handler: callable which is supplied subparsers object when invoked - :param title: title of the sub-commands group in help output - :param description: description of the group in help output - :param help: a help string giving an overview of available sub-commands - """ - - def __init__(self, name, dest=None, handler=None, - title=None, description=None, help=None): - """Construct an sub-command parsing option. - - This behaves similarly to other Opt sub-classes but adds a - 'handler' argument. The handler is a callable which is supplied - an subparsers object when invoked. The add_parser() method on - this subparsers object can be used to register parsers for - sub-commands. - """ - super(SubCommandOpt, self).__init__(name, type=types.String(), - dest=dest, help=help) - self.handler = handler - self.title = title - self.description = description - - def _add_to_cli(self, parser, group=None): - """Add argparse sub-parsers and invoke the handler method.""" - dest = self.dest - if group is not None: - dest = group.name + '_' + dest - - subparsers = parser.add_subparsers(dest=dest, - title=self.title, - description=self.description, - help=self.help) - # NOTE(jd) Set explicitly to True for Python 3 - # See http://bugs.python.org/issue9253 for context - subparsers.required = True - - if self.handler is not None: - self.handler(subparsers) - - -class _ConfigFileOpt(Opt): - - """The --config-file option. - - This is an private option type which handles the special processing - required for --config-file options. - - As each --config-file option is encountered on the command line, we - parse the file and store the parsed values in the _Namespace object. - 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): - - """An argparse action for --config-file. - - As each --config-file option is encountered, this action adds the - value to the config_file attribute on the _Namespace object but also - parses the configuration file and stores the values found also in - the _Namespace object. - """ - - def __call__(self, parser, namespace, values, option_string=None): - """Handle a --config-file command line argument. - - :raises: ConfigFileParseError, ConfigFileValueError - """ - if getattr(namespace, self.dest, None) is None: - setattr(namespace, self.dest, []) - items = getattr(namespace, self.dest) - items.append(values) - - ConfigParser._parse_file(values, namespace) - - def __init__(self, name, **kwargs): - super(_ConfigFileOpt, self).__init__(name, lambda x: x, **kwargs) - - def _get_argparse_kwargs(self, group, **kwargs): - """Extends the base argparse keyword dict for the config file opt.""" - kwargs = super(_ConfigFileOpt, self)._get_argparse_kwargs(group) - kwargs['action'] = self.ConfigFileAction - return kwargs - - -class _ConfigDirOpt(Opt): - - """The --config-dir option. - - This is an private option type which handles the special processing - required for --config-dir options. - - As each --config-dir option is encountered on the command line, we - parse the files in that directory and store the parsed values in the - _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): - - """An argparse action for --config-dir. - - As each --config-dir option is encountered, this action sets the - config_dir attribute on the _Namespace object but also parses the - configuration files and stores the values found also in the - _Namespace object. - """ - - def __call__(self, parser, namespace, values, option_string=None): - """Handle a --config-dir command line argument. - - :raises: ConfigFileParseError, ConfigFileValueError, - ConfigDirNotFoundError - """ - namespace._config_dirs.append(values) - setattr(namespace, self.dest, values) - - values = os.path.expanduser(values) - - if not os.path.exists(values): - raise ConfigDirNotFoundError(values) - - config_dir_glob = os.path.join(values, '*.conf') - - for config_file in sorted(glob.glob(config_dir_glob)): - ConfigParser._parse_file(config_file, namespace) - - def __init__(self, name, **kwargs): - super(_ConfigDirOpt, self).__init__(name, type=types.List(), - **kwargs) - - def _get_argparse_kwargs(self, group, **kwargs): - """Extends the base argparse keyword dict for the config dir option.""" - kwargs = super(_ConfigDirOpt, self)._get_argparse_kwargs(group) - kwargs['action'] = self.ConfigDirAction - return kwargs - - -class OptGroup(object): - - """Represents a group of opts. - - CLI opts in the group are automatically prefixed with the group name. - - Each group corresponds to a section in config files. - - An OptGroup object has no public methods, but has a number of public string - properties: - - .. py:attribute:: name - - the name of the group - - .. py:attribute:: title - - the group title as displayed in --help - - .. py:attribute:: help - - the group description as displayed in --help - - :param name: the group name - :param title: the group title for --help - :param help: the group description for --help - """ - - def __init__(self, name, title=None, help=None): - """Constructs an OptGroup object.""" - self.name = name - self.title = "%s options" % name if title is None else title - self.help = help - - self._opts = {} # dict of dicts of (opt:, override:, default:) - self._argparse_group = None - - def _register_opt(self, opt, cli=False): - """Add an opt to this group. - - :param opt: an Opt object - :param cli: whether this is a CLI option - :returns: False if previously registered, True otherwise - :raises: DuplicateOptError if a naming conflict is detected - """ - if _is_opt_registered(self._opts, opt): - return False - - self._opts[opt.dest] = {'opt': opt, 'cli': cli} - - return True - - def _unregister_opt(self, opt): - """Remove an opt from this group. - - :param opt: an Opt object - """ - if opt.dest in self._opts: - del self._opts[opt.dest] - - def _get_argparse_group(self, parser): - if self._argparse_group is None: - """Build an argparse._ArgumentGroup for this group.""" - self._argparse_group = parser.add_argument_group(self.title, - self.help) - return self._argparse_group - - def _clear(self): - """Clear this group's option parsing state.""" - self._argparse_group = None - - -class ParseError(iniparser.ParseError): - def __init__(self, msg, lineno, line, filename): - super(ParseError, self).__init__(msg, lineno, line) - self.filename = filename - - def __str__(self): - return 'at %s:%d, %s: %r' % (self.filename, self.lineno, - self.msg, self.line) - - -class ConfigParser(iniparser.BaseParser): - """Parses a single config file, populating 'sections' to look like: - - {'DEFAULT': {'key': [value, ...], ...}, - ...} - - Also populates self._normalized which looks the same but with normalized - section names. - """ - - def __init__(self, filename, sections): - super(ConfigParser, self).__init__() - self.filename = filename - self.sections = sections - self._normalized = None - self.section = None - - def _add_normalized(self, normalized): - self._normalized = normalized - - def parse(self): - with open(self.filename) as f: - return super(ConfigParser, self).parse(f) - - def new_section(self, section): - self.section = section - self.sections.setdefault(self.section, {}) - - if self._normalized is not None: - self._normalized.setdefault(_normalize_group_name(self.section), - {}) - - def assignment(self, key, value): - if not self.section: - raise self.error_no_section() - - value = '\n'.join(value) - - def append(sections, section): - sections[section].setdefault(key, []) - sections[section][key].append(value) - - append(self.sections, self.section) - if self._normalized is not None: - append(self._normalized, _normalize_group_name(self.section)) - - def parse_exc(self, msg, lineno, line=None): - return ParseError(msg, lineno, line, self.filename) - - def error_no_section(self): - return self.parse_exc('Section must be started before assignment', - self.lineno) - - @classmethod - def _parse_file(cls, config_file, namespace): - """Parse a config file and store any values in the namespace. - - :raises: ConfigFileParseError, ConfigFileValueError - """ - config_file = _fixpath(config_file) - - sections = {} - normalized = {} - parser = cls(config_file, sections) - parser._add_normalized(normalized) - - try: - parser.parse() - except iniparser.ParseError as pe: - raise ConfigFileParseError(pe.filename, str(pe)) - except IOError as err: - if err.errno == errno.ENOENT: - namespace._file_not_found(config_file) - return - if err.errno == errno.EACCES: - namespace._file_permission_denied(config_file) - return - raise - - namespace._add_parsed_config_file(sections, normalized) - namespace._parse_cli_opts_from_config_file(sections, normalized) - - -@removals.remove(version='3.4', removal_version='4.0') -class MultiConfigParser(object): - """A ConfigParser which handles multi-opts. - - All methods in this class which accept config names should treat a section - name of None as 'DEFAULT'. - - This class was deprecated in Mitaka and should be removed in Ocata. - _Namespace holds values, ConfigParser._parse_file reads one file into a - _Namespace and ConfigOpts._parse_config_files reads multiple files into a - _Namespace. - """ - - _deprecated_opt_message = _LW('Option "%(dep_option)s" from group ' - '"%(dep_group)s" is deprecated. Use option ' - '"%(option)s" from group "%(group)s".') - - def __init__(self): - self.parsed = [] - self._normalized = [] - self._emitted_deprecations = set() - - def read(self, config_files): - read_ok = [] - - for filename in config_files: - sections = {} - normalized = {} - parser = ConfigParser(filename, sections) - parser._add_normalized(normalized) - - try: - parser.parse() - except IOError: - continue - self._add_parsed_config_file(sections, normalized) - read_ok.append(filename) - - return read_ok - - def _add_parsed_config_file(self, sections, normalized): - """Add a parsed config file to the list of parsed files. - - :param sections: a mapping of section name to dicts of config values - :param normalized: sections mapping with section names normalized - :raises: ConfigFileValueError - """ - self.parsed.insert(0, sections) - self._normalized.insert(0, normalized) - - def get(self, names, multi=False): - return self._get(names, multi=multi) - - def _get(self, names, multi=False, normalized=False, current_name=None): - """Fetch a config file value from the parsed files. - - :param names: a list of (section, name) tuples - :param multi: a boolean indicating whether to return multiple values - :param normalized: whether to normalize group names to lowercase - :param current_name: current name in tuple being checked - """ - rvalue = [] - - def normalize(name): - if name is None: - name = 'DEFAULT' - return _normalize_group_name(name) if normalized else name - - names = [(normalize(section), name) for section, name in names] - - for sections in (self._normalized if normalized else self.parsed): - for section, name in names: - if section not in sections: - continue - if name in sections[section]: - current_name = current_name or names[0] - self._check_deprecated((section, name), current_name, - names[1:]) - val = sections[section][name] - if multi: - rvalue = val + rvalue - else: - return val - if multi and rvalue != []: - return rvalue - raise KeyError - - def _check_deprecated(self, name, current, deprecated): - """Check for usage of deprecated names. - - :param name: A tuple of the form (group, name) representing the group - and name where an opt value was found. - :param current: A tuple of the form (group, name) representing the - current name for an option. - :param deprecated: A list of tuples with the same format as the name - param which represent any deprecated names for an option. - If the name param matches any entries in this list a - deprecation warning will be logged. - """ - if name in deprecated and name not in self._emitted_deprecations: - self._emitted_deprecations.add(name) - current = (current[0] or 'DEFAULT', current[1]) - # NOTE(bnemec): Not using versionutils for this to avoid a - # circular dependency between oslo.config and whatever library - # versionutils ends up in. - LOG.warning(self._deprecated_opt_message, - {'dep_option': name[1], 'dep_group': name[0], - 'option': current[1], 'group': current[0]}) - - -class _Namespace(argparse.Namespace): - """An argparse namespace which also stores config file values. - - As we parse command line arguments, the values get set as attributes - on a namespace object. However, we also want to parse config files as - they are specified on the command line and collect the values alongside - the option values parsed from the command line. - - Note, we don't actually assign values from config files as attributes - on the namespace because config file options be registered after the - command line has been parsed, so we may not know how to properly parse - or convert a config file value at this point. - """ - - _deprecated_opt_message = _LW('Option "%(dep_option)s" from group ' - '"%(dep_group)s" is deprecated. Use option ' - '"%(option)s" from group "%(group)s".') - - def __init__(self, conf): - self._conf = conf - self._parsed = [] - self._normalized = [] - self._emitted_deprecations = set() - self._files_not_found = [] - self._files_permission_denied = [] - self._config_dirs = [] - - def _parse_cli_opts_from_config_file(self, sections, normalized): - """Parse CLI options from a config file. - - CLI options are special - we require they be registered before the - command line is parsed. This means that as we parse config files, we - can go ahead and apply the appropriate option-type specific conversion - to the values in config files for CLI options. We can't do this for - non-CLI options, because the schema describing those options may not be - registered until after the config files are parsed. - - This method relies on that invariant in order to enforce proper - priority of option values - i.e. that the order in which an option - value is parsed, whether the value comes from the CLI or a config file, - determines which value specified for a given option wins. - - The way we implement this ordering is that as we parse each config - file, we look for values in that config file for CLI options only. Any - values for CLI options found in the config file are treated like they - had appeared on the command line and set as attributes on the namespace - objects. Values in later config files or on the command line will - override values found in this file. - """ - namespace = _Namespace(self._conf) - namespace._add_parsed_config_file(sections, normalized) - - for opt, group in self._conf._all_cli_opts(): - group_name = group.name if group is not None else None - try: - value = opt._get_from_namespace(namespace, group_name) - except KeyError: - continue - except ValueError as ve: - raise ConfigFileValueError( - "Value for option %s is not valid: %s" - % (opt.name, str(ve))) - - if group_name is None: - dest = opt.dest - else: - dest = group_name + '_' + opt.dest - - if opt.multi: - if getattr(self, dest, None) is None: - setattr(self, dest, []) - values = getattr(self, dest) - values.extend(value) - else: - setattr(self, dest, value) - - def _add_parsed_config_file(self, sections, normalized): - """Add a parsed config file to the list of parsed files. - - :param sections: a mapping of section name to dicts of config values - :param normalized: sections mapping with section names normalized - :raises: ConfigFileValueError - """ - self._parsed.insert(0, sections) - self._normalized.insert(0, normalized) - - def _file_not_found(self, config_file): - """Record that we were unable to open a config file. - - :param config_file: the path to the failed file - """ - self._files_not_found.append(config_file) - - def _file_permission_denied(self, config_file): - """Record that we have no permission to open a config file. - - :param config_file: the path to the failed file - """ - self._files_permission_denied.append(config_file) - - def _get_cli_value(self, names, positional=False): - """Fetch a CLI option value. - - Look up the value of a CLI option. The value itself may have come from - parsing the command line or parsing config files specified on the - command line. Type conversion have already been performed for CLI - options at this point. - - :param names: a list of (section, name) tuples - :param positional: whether this is a positional option - """ - for group_name, name in names: - name = name if group_name is None else group_name + '_' + name - value = getattr(self, name, None) - if value is not None: - # argparse ignores default=None for nargs='*' and returns [] - if positional and not value: - continue - - return value - - raise KeyError - - def _get_file_value( - self, names, multi=False, normalized=False, current_name=None): - """Fetch a config file value from the parsed files. - - :param names: a list of (section, name) tuples - :param multi: a boolean indicating whether to return multiple values - :param normalized: whether to normalize group names to lowercase - :param current_name: current name in tuple being checked - """ - rvalue = [] - - def normalize(name): - if name is None: - name = 'DEFAULT' - return _normalize_group_name(name) if normalized else name - - names = [(normalize(section), name) for section, name in names] - - for sections in (self._normalized if normalized else self._parsed): - for section, name in names: - if section not in sections: - continue - if name in sections[section]: - current_name = current_name or names[0] - self._check_deprecated((section, name), current_name, - names[1:]) - val = sections[section][name] - if multi: - rvalue = val + rvalue - else: - return val - if multi and rvalue != []: - return rvalue - raise KeyError - - def _check_deprecated(self, name, current, deprecated): - """Check for usage of deprecated names. - - :param name: A tuple of the form (group, name) representing the group - and name where an opt value was found. - :param current: A tuple of the form (group, name) representing the - current name for an option. - :param deprecated: A list of tuples with the same format as the name - param which represent any deprecated names for an option. - If the name param matches any entries in this list a - deprecation warning will be logged. - """ - if name in deprecated and name not in self._emitted_deprecations: - self._emitted_deprecations.add(name) - current = (current[0] or 'DEFAULT', current[1]) - # NOTE(bnemec): Not using versionutils for this to avoid a - # circular dependency between oslo.config and whatever library - # versionutils ends up in. - LOG.warning(self._deprecated_opt_message, - {'dep_option': name[1], 'dep_group': name[0], - 'option': current[1], 'group': current[0]}) - - def _get_value(self, names, multi=False, positional=False, - current_name=None, normalized=True): - """Fetch a value from config files. - - Multiple names for a given configuration option may be supplied so - that we can transparently handle files containing deprecated option - names or groups. - - :param names: a list of (section, name) tuples - :param positional: whether this is a positional option - :param multi: a boolean indicating whether to return multiple values - :param normalized: whether to normalize group names to lowercase - """ - try: - return self._get_cli_value(names, positional) - except KeyError: - names = [(g if g is not None else 'DEFAULT', n) for g, n in names] - values = self._get_file_value( - names, multi=multi, normalized=normalized, - current_name=current_name) - return values if multi else values[-1] - - def _sections(self): - for sections in self._parsed: - for section in sections: - yield section - - -class _CachedArgumentParser(argparse.ArgumentParser): - - """class for caching/collecting command line arguments. - - It also sorts the arguments before initializing the ArgumentParser. - We need to do this since ArgumentParser by default does not sort - the argument options and the only way to influence the order of - arguments in '--help' is to ensure they are added in the sorted - order. - """ - - def __init__(self, prog=None, usage=None, **kwargs): - super(_CachedArgumentParser, self).__init__(prog, usage, **kwargs) - self._args_cache = {} - - def add_parser_argument(self, container, *args, **kwargs): - values = [] - if container in self._args_cache: - values = self._args_cache[container] - values.append({'args': args, 'kwargs': kwargs}) - self._args_cache[container] = values - - def initialize_parser_arguments(self): - # NOTE(mfedosin): The code below looks a little bit weird, but - # it's done because we need to sort only optional opts and do - # not touch positional. For the reason optional opts go first in - # the values we only need to find an index of the first positional - # option and then sort the values slice. - for container, values in six.iteritems(self._args_cache): - index = 0 - has_positional = False - for index, argument in enumerate(values): - if not argument['args'][0].startswith('-'): - has_positional = True - break - size = index if has_positional else len(values) - values[:size] = sorted(values[:size], key=lambda x: x['args']) - for argument in values: - try: - container.add_argument(*argument['args'], - **argument['kwargs']) - except argparse.ArgumentError as e: - raise DuplicateOptError(e) - self._args_cache = {} - - def parse_args(self, args=None, namespace=None): - self.initialize_parser_arguments() - return super(_CachedArgumentParser, self).parse_args(args, namespace) - - def print_help(self, file=None): - self.initialize_parser_arguments() - super(_CachedArgumentParser, self).print_help(file) - - def print_usage(self, file=None): - self.initialize_parser_arguments() - super(_CachedArgumentParser, self).print_usage(file) - - -class ConfigOpts(collections.Mapping): - - """Config options which may be set on the command line or in config files. - - ConfigOpts is a configuration option manager with APIs for registering - option schemas, grouping options, parsing option values and retrieving - the values of options. - - It has built-in support for :oslo.config:option:`config_file` and - :oslo.config:option:`config_dir` options. - - """ - disallow_names = ('project', 'prog', 'version', - 'usage', 'default_config_files') - - def __init__(self): - """Construct a ConfigOpts object.""" - self._opts = {} # dict of dicts of (opt:, override:, default:) - self._groups = {} - - self._args = None - - self._oparser = None - self._namespace = None - self._mutable_ns = None - self._mutate_hooks = set([]) - self.__cache = {} - self._config_opts = [] - self._cli_opts = collections.deque() - self._validate_default_values = False - - def _pre_setup(self, project, prog, version, usage, default_config_files): - """Initialize a ConfigCliParser object for option parsing.""" - - if prog is None: - prog = os.path.basename(sys.argv[0]) - - if default_config_files is None: - default_config_files = find_config_files(project, prog) - - self._oparser = _CachedArgumentParser(prog=prog, usage=usage) - - if version is not None: - self._oparser.add_parser_argument(self._oparser, - '--version', - action='version', - version=version) - - return prog, default_config_files - - @staticmethod - def _make_config_options(default_config_files): - return [ - _ConfigFileOpt('config-file', - default=default_config_files, - metavar='PATH', - help=('Path to a config file to use. Multiple ' - 'config files can be specified, with values ' - 'in later files taking precedence. Defaults ' - 'to %(default)s.')), - _ConfigDirOpt('config-dir', - metavar='DIR', - help='Path to a config directory to pull *.conf ' - 'files from. This file set is sorted, so as to ' - 'provide a predictable parse order if ' - 'individual options are over-ridden. The set ' - 'is parsed after the file(s) specified via ' - 'previous --config-file, arguments hence ' - 'over-ridden options in the directory take ' - 'precedence.'), - ] - - def _setup(self, project, prog, version, usage, default_config_files): - """Initialize a ConfigOpts object for option parsing.""" - - self._config_opts = self._make_config_options(default_config_files) - self.register_cli_opts(self._config_opts) - - self.project = project - self.prog = prog - self.version = version - self.usage = usage - self.default_config_files = default_config_files - - def __clear_cache(f): - @functools.wraps(f) - def __inner(self, *args, **kwargs): - if kwargs.pop('clear_cache', True): - result = f(self, *args, **kwargs) - self.__cache.clear() - return result - else: - return f(self, *args, **kwargs) - - return __inner - - def __call__(self, - args=None, - project=None, - prog=None, - version=None, - usage=None, - default_config_files=None, - validate_default_values=False): - """Parse command line arguments and config files. - - Calling a ConfigOpts object causes the supplied command line arguments - and config files to be parsed, causing opt values to be made available - as attributes of the object. - - The object may be called multiple times, each time causing the previous - set of values to be overwritten. - - Automatically registers the --config-file option with either a supplied - list of default config files, or a list from find_config_files(). - - If the --config-dir option is set, any *.conf files from this - directory are pulled in, after all the file(s) specified by the - --config-file option. - - :param args: command line arguments (defaults to sys.argv[1:]) - :param project: the toplevel project name, used to locate config files - :param prog: the name of the program (defaults to sys.argv[0] basename) - :param version: the program version (for --version) - :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 - :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError, - ConfigFilesPermissionDeniedError, - RequiredOptError, DuplicateOptError - """ - self.clear() - - self._validate_default_values = validate_default_values - - prog, default_config_files = self._pre_setup(project, - prog, - version, - usage, - default_config_files) - - self._setup(project, prog, version, usage, default_config_files) - - self._namespace = self._parse_cli_opts(args if args is not None - else sys.argv[1:]) - if self._namespace._files_not_found: - raise ConfigFilesNotFoundError(self._namespace._files_not_found) - if self._namespace._files_permission_denied: - raise ConfigFilesPermissionDeniedError( - self._namespace._files_permission_denied) - - self._check_required_opts() - - def __getattr__(self, name): - """Look up an option value and perform string substitution. - - :param name: the opt name (or 'dest', more precisely) - :returns: the option value (after string substitution) or a GroupAttr - :raises: ValueError or NoSuchOptError - """ - try: - return self._get(name) - except ValueError: - raise - except Exception: - raise NoSuchOptError(name) - - def __getitem__(self, key): - """Look up an option value and perform string substitution.""" - return self.__getattr__(key) - - def __contains__(self, key): - """Return True if key is the name of a registered opt or group.""" - return key in self._opts or key in self._groups - - def __iter__(self): - """Iterate over all registered opt and group names.""" - for key in itertools.chain(self._opts.keys(), self._groups.keys()): - yield key - - def __len__(self): - """Return the number of options and option groups.""" - return len(self._opts) + len(self._groups) - - def reset(self): - """Clear the object state and unset overrides and defaults.""" - self._unset_defaults_and_overrides() - self.clear() - - @__clear_cache - def clear(self): - """Clear the state of the object to before it was called. - - Any subparsers added using the add_cli_subparsers() will also be - removed as a side-effect of this method. - """ - self._args = None - self._oparser = None - self._namespace = None - self._mutable_ns = None - # Keep _mutate_hooks - self._validate_default_values = False - self.unregister_opts(self._config_opts) - for group in self._groups.values(): - group._clear() - - def _add_cli_opt(self, opt, group): - if {'opt': opt, 'group': group} in self._cli_opts: - return - if opt.positional: - self._cli_opts.append({'opt': opt, 'group': group}) - else: - self._cli_opts.appendleft({'opt': opt, 'group': group}) - - @__clear_cache - def register_opt(self, opt, group=None, cli=False): - """Register an option schema. - - Registering an option schema makes any option value which is previously - or subsequently parsed from the command line or config files available - as an attribute of this object. - - :param opt: an instance of an Opt sub-class - :param group: an optional OptGroup object or group name - :param cli: whether this is a CLI option - :return: False if the opt was already registered, True otherwise - :raises: DuplicateOptError - """ - if group is not None: - group = self._get_group(group, autocreate=True) - if cli: - self._add_cli_opt(opt, group) - return group._register_opt(opt, cli) - - # NOTE(gcb) We can't use some names which are same with attributes of - # Opts in default group. They includes project, prog, version, usage - # and default_config_files. - if group is None: - if opt.name in self.disallow_names: - raise ValueError('Name %s was reserved for oslo.config.' - % opt.name) - - if cli: - self._add_cli_opt(opt, None) - - if _is_opt_registered(self._opts, opt): - return False - - self._opts[opt.dest] = {'opt': opt, 'cli': cli} - - return True - - @__clear_cache - def register_opts(self, opts, group=None): - """Register multiple option schemas at once.""" - for opt in opts: - self.register_opt(opt, group, clear_cache=False) - - @__clear_cache - def register_cli_opt(self, opt, group=None): - """Register a CLI option schema. - - CLI option schemas must be registered before the command line and - config files are parsed. This is to ensure that all CLI options are - shown in --help and option validation works as expected. - - :param opt: an instance of an Opt sub-class - :param group: an optional OptGroup object or group name - :return: False if the opt was already registered, True otherwise - :raises: DuplicateOptError, ArgsAlreadyParsedError - """ - if self._args is not None: - raise ArgsAlreadyParsedError("cannot register CLI option") - - return self.register_opt(opt, group, cli=True, clear_cache=False) - - @__clear_cache - def register_cli_opts(self, opts, group=None): - """Register multiple CLI option schemas at once.""" - for opt in opts: - self.register_cli_opt(opt, group, clear_cache=False) - - def register_group(self, group): - """Register an option group. - - An option group must be registered before options can be registered - with the group. - - :param group: an OptGroup object - """ - if group.name in self._groups: - return - - self._groups[group.name] = copy.copy(group) - - @__clear_cache - def unregister_opt(self, opt, group=None): - """Unregister an option. - - :param opt: an Opt object - :param group: an optional OptGroup object or group name - :raises: ArgsAlreadyParsedError, NoSuchGroupError - """ - if self._args is not None: - raise ArgsAlreadyParsedError("reset before unregistering options") - - remitem = None - for item in self._cli_opts: - if (item['opt'].dest == opt.dest and - (group is None or - self._get_group(group).name == item['group'].name)): - remitem = item - break - if remitem is not None: - self._cli_opts.remove(remitem) - - if group is not None: - self._get_group(group)._unregister_opt(opt) - elif opt.dest in self._opts: - del self._opts[opt.dest] - - @__clear_cache - def unregister_opts(self, opts, group=None): - """Unregister multiple CLI option schemas at once.""" - for opt in opts: - self.unregister_opt(opt, group, clear_cache=False) - - def import_opt(self, name, module_str, group=None): - """Import an option definition from a module. - - Import a module and check that a given option is registered. - - This is intended for use with global configuration objects - like cfg.CONF where modules commonly register options with - CONF at module load time. If one module requires an option - defined by another module it can use this method to explicitly - declare the dependency. - - :param name: the name/dest of the opt - :param module_str: the name of a module to import - :param group: an option OptGroup object or group name - :raises: NoSuchOptError, NoSuchGroupError - """ - __import__(module_str) - self._get_opt_info(name, group) - - def import_group(self, group, module_str): - """Import an option group from a module. - - Import a module and check that a given option group is registered. - - This is intended for use with global configuration objects - like cfg.CONF where modules commonly register options with - CONF at module load time. If one module requires an option group - defined by another module it can use this method to explicitly - declare the dependency. - - :param group: an option OptGroup object or group name - :param module_str: the name of a module to import - :raises: ImportError, NoSuchGroupError - """ - __import__(module_str) - self._get_group(group) - - @__clear_cache - def set_override(self, name, override, group=None, enforce_type=False): - """Override an opt value. - - Override the command line, config file and default values of a - given option. - - :param name: the name/dest of the opt - :param override: the override value - :param group: an option OptGroup object or group name - :param enforce_type: a boolean whether to convert the override - value to the option's type, None is *not* converted even - if enforce_type is True. - :raises: NoSuchOptError, NoSuchGroupError - """ - opt_info = self._get_opt_info(name, group) - if enforce_type and override is not None: - opt_info['override'] = self._convert_value(override, - opt_info['opt']) - else: - opt_info['override'] = override - - @__clear_cache - def set_default(self, name, default, group=None, enforce_type=False): - """Override an opt's default value. - - Override the default value of given option. A command line or - config file value will still take precedence over this default. - - :param name: the name/dest of the opt - :param default: the default value - :param group: an option OptGroup object or group name - :param enforce_type: a boolean whether to convert the default - value to the option's type, None is *not* converted even - if enforce_type is True. - :raises: NoSuchOptError, NoSuchGroupError - """ - opt_info = self._get_opt_info(name, group) - if enforce_type and default is not None: - opt_info['default'] = self._convert_value(default, - opt_info['opt']) - else: - opt_info['default'] = default - - @__clear_cache - def clear_override(self, name, group=None): - """Clear an override an opt value. - - Clear a previously set override of the command line, config file - and default values of a given option. - - :param name: the name/dest of the opt - :param group: an option OptGroup object or group name - :raises: NoSuchOptError, NoSuchGroupError - """ - opt_info = self._get_opt_info(name, group) - opt_info.pop('override', None) - - @__clear_cache - def clear_default(self, name, group=None): - """Clear an override an opt's default value. - - Clear a previously set override of the default value of given option. - - :param name: the name/dest of the opt - :param group: an option OptGroup object or group name - :raises: NoSuchOptError, NoSuchGroupError - """ - opt_info = self._get_opt_info(name, group) - opt_info.pop('default', None) - - def _all_opt_infos(self): - """A generator function for iteration opt infos.""" - for info in self._opts.values(): - yield info, None - for group in self._groups.values(): - for info in group._opts.values(): - yield info, group - - def _all_cli_opts(self): - """A generator function for iterating CLI opts.""" - for item in self._cli_opts: - yield item['opt'], item['group'] - - def _unset_defaults_and_overrides(self): - """Unset any default or override on all options.""" - for info, group in self._all_opt_infos(): - info.pop('default', None) - info.pop('override', None) - - @property - def config_dirs(self): - if self._namespace is None: - return [] - return self._namespace._config_dirs - - def find_file(self, name): - """Locate a file located alongside the config files. - - Search for a file with the supplied basename in the directories - which we have already loaded config files from and other known - configuration directories. - - The directory, if any, supplied by the config_dir option is - searched first. Then the config_file option is iterated over - and each of the base directories of the config_files values - are searched. Failing both of these, the standard directories - searched by the module level find_config_files() function is - used. The first matching file is returned. - - :param name: the filename, for example 'policy.json' - :returns: the path to a matching file, or None - """ - if not self._namespace: - raise NotInitializedError() - dirs = [] - if self._namespace._config_dirs: - for directory in self._namespace._config_dirs: - dirs.append(_fixpath(directory)) - - for cf in reversed(self.config_file): - dirs.append(os.path.dirname(_fixpath(cf))) - - dirs.extend(_get_config_dirs(self.project)) - - return _search_dirs(dirs, name) - - def log_opt_values(self, logger, lvl): - """Log the value of all registered opts. - - It's often useful for an app to log its configuration to a log file at - startup for debugging. This method dumps to the entire config state to - the supplied logger at a given log level. - - :param logger: a logging.Logger object - :param lvl: the log level (for example logging.DEBUG) arg to - logger.log() - """ - logger.log(lvl, "*" * 80) - logger.log(lvl, "Configuration options gathered from:") - logger.log(lvl, "command line args: %s", self._args) - logger.log(lvl, "config files: %s", - hasattr(self, 'config_file') and self.config_file or []) - logger.log(lvl, "=" * 80) - - def _sanitize(opt, value): - """Obfuscate values of options declared secret.""" - return value if not opt.secret else '*' * 4 - - for opt_name in sorted(self._opts): - opt = self._get_opt_info(opt_name)['opt'] - logger.log(lvl, "%-30s = %s", opt_name, - _sanitize(opt, getattr(self, opt_name))) - - for group_name in self._groups: - group_attr = self.GroupAttr(self, self._get_group(group_name)) - for opt_name in sorted(self._groups[group_name]._opts): - opt = self._get_opt_info(opt_name, group_name)['opt'] - logger.log(lvl, "%-30s = %s", - "%s.%s" % (group_name, opt_name), - _sanitize(opt, getattr(group_attr, opt_name))) - - logger.log(lvl, "*" * 80) - - def print_usage(self, file=None): - """Print the usage message for the current program. - - This method is for use after all CLI options are known - registered using __call__() method. If this method is called - before the __call__() is invoked, it throws NotInitializedError - - :param file: the File object (if None, output is on sys.stdout) - :raises: NotInitializedError - """ - if not self._oparser: - raise NotInitializedError() - self._oparser.print_usage(file) - - def print_help(self, file=None): - """Print the help message for the current program. - - This method is for use after all CLI options are known - registered using __call__() method. If this method is called - before the __call__() is invoked, it throws NotInitializedError - - :param file: the File object (if None, output is on sys.stdout) - :raises: NotInitializedError - """ - if not self._oparser: - raise NotInitializedError() - self._oparser.print_help(file) - - def _get(self, name, group=None, namespace=None): - if isinstance(group, OptGroup): - key = (group.name, name) - else: - key = (group, name) - if namespace is None: - try: - return self.__cache[key] - except KeyError: # nosec: Valid control flow instruction - pass - value = self._do_get(name, group, namespace) - self.__cache[key] = value - return value - - def _do_get(self, name, group=None, namespace=None): - """Look up an option value. - - :param name: the opt name (or 'dest', more precisely) - :param group: an OptGroup - :param namespace: the namespace object to get the option value from - :returns: the option value, or a GroupAttr object - :raises: NoSuchOptError, NoSuchGroupError, ConfigFileValueError, - TemplateSubstitutionError - """ - if group is None and name in self._groups: - return self.GroupAttr(self, self._get_group(name)) - - info = self._get_opt_info(name, group) - opt = info['opt'] - - if isinstance(opt, SubCommandOpt): - return self.SubCommandAttr(self, group, opt.dest) - - if 'override' in info: - return self._substitute(info['override']) - - def convert(value): - return self._convert_value( - self._substitute(value, group, namespace), opt) - - if opt.mutable and namespace is None: - namespace = self._mutable_ns - if namespace is None: - namespace = self._namespace - if namespace is not None: - group_name = group.name if group else None - try: - return convert(opt._get_from_namespace(namespace, group_name)) - except KeyError: # nosec: Valid control flow instruction - pass - except ValueError as ve: - raise ConfigFileValueError( - "Value for option %s is not valid: %s" - % (opt.name, str(ve))) - - if 'default' in info: - return self._substitute(info['default']) - - if self._validate_default_values: - if opt.default is not None: - try: - convert(opt.default) - except ValueError as e: - raise ConfigFileValueError( - "Default value for option %s is not valid: %s" - % (opt.name, str(e))) - - if opt.default is not None: - return convert(opt.default) - - return None - - def _substitute(self, value, group=None, namespace=None): - """Perform string template substitution. - - Substitute any template variables (for example $foo, ${bar}) in - the supplied string value(s) with opt values. - - :param value: the string value, or list of string values - :param group: the group that retrieves the option value from - :param namespace: the namespace object that retrieves the option - value from - :returns: the substituted string(s) - """ - if isinstance(value, list): - return [self._substitute(i, group=group, namespace=namespace) - for i in value] - elif isinstance(value, str): - # Treat a backslash followed by the dollar sign "\$" - # the same as the string template escape "$$" as it is - # a bit more natural for users - if '\$' in value: - value = value.replace('\$', '$$') - tmpl = self.Template(value) - ret = tmpl.safe_substitute( - self.StrSubWrapper(self, group=group, namespace=namespace)) - return ret - elif isinstance(value, dict): - # Substitute template variables in both key and value - return {self._substitute(key, group=group, namespace=namespace): - self._substitute(val, group=group, namespace=namespace) - for key, val in value.items()} - else: - return value - - class Template(string.Template): - idpattern = r'[_a-z][\._a-z0-9]*' - - def _convert_value(self, value, opt): - """Perform value type conversion. - - Converts values using option's type. Handles cases when value is - actually a list of values (for example for multi opts). - - :param value: the string value, or list of string values - :param opt: option definition (instance of Opt class or its subclasses) - :returns: converted value - """ - if opt.multi: - return [opt.type(v) for v in value] - else: - return opt.type(value) - - def _get_group(self, group_or_name, autocreate=False): - """Looks up a OptGroup object. - - Helper function to return an OptGroup given a parameter which can - either be the group's name or an OptGroup object. - - The OptGroup object returned is from the internal dict of OptGroup - objects, which will be a copy of any OptGroup object that users of - the API have access to. - - If autocreate is True, the group will be created if it's not found. If - group is an instance of OptGroup, that same instance will be - registered, otherwise a new instance of OptGroup will be created. - - :param group_or_name: the group's name or the OptGroup object itself - :param autocreate: whether to auto-create the group if it's not found - :raises: NoSuchGroupError - """ - group = group_or_name if isinstance(group_or_name, OptGroup) else None - group_name = group.name if group else group_or_name - - if group_name not in self._groups: - if not autocreate: - raise NoSuchGroupError(group_name) - - self.register_group(group or OptGroup(name=group_name)) - - return self._groups[group_name] - - def _get_opt_info(self, opt_name, group=None): - """Return the (opt, override, default) dict for an opt. - - :param opt_name: an opt name/dest - :param group: an optional group name or OptGroup object - :raises: NoSuchOptError, NoSuchGroupError - """ - if group is None: - opts = self._opts - else: - group = self._get_group(group) - opts = group._opts - - if opt_name not in opts: - raise NoSuchOptError(opt_name, group) - - return opts[opt_name] - - def _check_required_opts(self, namespace=None): - """Check that all opts marked as required have values specified. - - :param namespace: the namespace object be checked the required options - :raises: RequiredOptError - """ - for info, group in self._all_opt_infos(): - opt = info['opt'] - - if opt.required: - if 'default' in info or 'override' in info: - continue - - if self._get(opt.dest, group, namespace) is None: - raise RequiredOptError(opt.name, group) - - def _parse_cli_opts(self, args): - """Parse command line options. - - Initializes the command line option parser and parses the supplied - command line arguments. - - :param args: the command line arguments - :returns: a _Namespace object containing the parsed option values - :raises: SystemExit, DuplicateOptError - ConfigFileParseError, ConfigFileValueError - - """ - self._args = args - for opt, group in self._all_cli_opts(): - opt._add_to_cli(self._oparser, group) - - return self._parse_config_files() - - def _parse_config_files(self): - """Parse configure files options. - - :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError, - ConfigFilesPermissionDeniedError, - RequiredOptError, DuplicateOptError - """ - namespace = _Namespace(self) - for arg in self._args: - if arg == '--config-file' or arg.startswith('--config-file='): - break - else: - for config_file in self.default_config_files: - ConfigParser._parse_file(config_file, namespace) - - self._oparser.parse_args(self._args, namespace) - - self._validate_cli_options(namespace) - - return namespace - - def _validate_cli_options(self, namespace): - for opt, group in sorted(self._all_cli_opts(), - key=lambda x: x[0].name): - group_name = group.name if group else None - try: - value = opt._get_from_namespace(namespace, group_name) - except KeyError: - continue - - value = self._substitute(value, group=group, namespace=namespace) - - try: - self._convert_value(value, opt) - except ValueError: - sys.stderr.write("argument --%s: Invalid %s value: %s\n" % ( - opt.dest, repr(opt.type), value)) - raise SystemExit - - def _reload_config_files(self): - namespace = self._parse_config_files() - if namespace._files_not_found: - raise ConfigFilesNotFoundError(namespace._files_not_found) - if namespace._files_permission_denied: - raise ConfigFilesPermissionDeniedError( - namespace._files_permission_denied) - self._check_required_opts(namespace) - return namespace - - @__clear_cache - def reload_config_files(self): - """Reload configure files and parse all options - - :return False if reload configure files failed or else return True - """ - - try: - namespace = self._reload_config_files() - except SystemExit as exc: - LOG.warning(_LW("Caught SystemExit while reloading configure " - "files with exit code: %d"), exc.code) - return False - except Error as err: - LOG.warning(_LW("Caught Error while reloading configure files: " - " %s"), err) - return False - else: - self._namespace = namespace - return True - - def register_mutate_hook(self, hook): - """Registers a hook to be called by mutate_config_files. - - :param hook: a function accepting this ConfigOpts object and a dict of - config mutations, as returned by mutate_config_files. - :return None - """ - self._mutate_hooks.add(hook) - - def mutate_config_files(self): - """Reload configure files and parse all options. - - Only options marked as 'mutable' will appear to change. - - Hooks are called in a NON-DETERMINISTIC ORDER. Do not expect hooks to - be called in the same order as they were added. - - :return {(None or 'group', 'optname'): (old_value, new_value), ... } - :raises Error if reloading fails - """ - self.__cache.clear() - - old_mutate_ns = self._mutable_ns or self._namespace - self._mutable_ns = self._reload_config_files() - self._warn_immutability() - fresh = self._diff_ns(old_mutate_ns, self._mutable_ns) - - def key_fn(item): - # Py3 won't sort heterogeneous types. Sort None as TAB which has a - # very low ASCII value. - (groupname, optname) = item[0] - return item[0] if groupname else ('\t', optname) - sorted_fresh = sorted(fresh.items(), key=key_fn) - for (groupname, optname), (old, new) in sorted_fresh: - groupname = groupname if groupname else 'DEFAULT' - LOG.info(_LI("Option %(group)s.%(option)s changed from " - "[%(old_val)s] to [%(new_val)s]"), - {'group': groupname, - 'option': optname, - 'old_val': old, - 'new_val': new}) - for hook in self._mutate_hooks: - hook(self, fresh) - return fresh - - def _warn_immutability(self): - """Check immutable opts have not changed. - - _do_get won't return the new values but presumably someone changed the - config file expecting them to change so we should warn them they won't. - """ - for info, group in self._all_opt_infos(): - opt = info['opt'] - if opt.mutable: - continue - groupname = group.name if group else 'DEFAULT' - try: - old = opt._get_from_namespace(self._namespace, groupname) - except KeyError: - old = None - try: - new = opt._get_from_namespace(self._mutable_ns, groupname) - except KeyError: - new = None - if old != new: - LOG.warning(_LW("Ignoring change to immutable option " - "%(group)s.%(option)s"), - {"group": groupname, "option": opt.name}) - - def _diff_ns(self, old_ns, new_ns): - """Compare mutable option values between two namespaces. - - This can be used to only reconfigure stateful sessions when necessary. - - :return {(None or 'group', 'optname'): (old_value, new_value), ... } - """ - diff = {} - for info, group in self._all_opt_infos(): - opt = info['opt'] - if not opt.mutable: - continue - groupname = group.name if group else None - try: - old = opt._get_from_namespace(old_ns, groupname) - except KeyError: - old = None - try: - new = opt._get_from_namespace(new_ns, groupname) - except KeyError: - new = None - if old != new: - diff[(groupname, opt.name)] = (old, new) - return diff - - def list_all_sections(self): - """List all sections from the configuration. - - Returns a sorted list of all section names found in the - configuration files, whether declared beforehand or not. - """ - s = set([]) - if self._mutable_ns: - s |= set(self._mutable_ns._sections()) - if self._namespace: - s |= set(self._namespace._sections()) - return sorted(s) - - class GroupAttr(collections.Mapping): - - """Helper class. - - Represents the option values of a group as a mapping and attributes. - """ - - def __init__(self, conf, group): - """Construct a GroupAttr object. - - :param conf: a ConfigOpts object - :param group: an OptGroup object - """ - self._conf = conf - self._group = group - - def __getattr__(self, name): - """Look up an option value and perform template substitution.""" - return self._conf._get(name, self._group) - - def __getitem__(self, key): - """Look up an option value and perform string substitution.""" - return self.__getattr__(key) - - def __contains__(self, key): - """Return True if key is the name of a registered opt or group.""" - return key in self._group._opts - - def __iter__(self): - """Iterate over all registered opt and group names.""" - for key in self._group._opts.keys(): - yield key - - def __len__(self): - """Return the number of options and option groups.""" - return len(self._group._opts) - - class SubCommandAttr(object): - - """Helper class. - - Represents the name and arguments of an argparse sub-parser. - """ - - def __init__(self, conf, group, dest): - """Construct a SubCommandAttr object. - - :param conf: a ConfigOpts object - :param group: an OptGroup object - :param dest: the name of the sub-parser - """ - self._conf = conf - self._group = group - self._dest = dest - - def __getattr__(self, name): - """Look up a sub-parser name or argument value.""" - if name == 'name': - name = self._dest - if self._group is not None: - name = self._group.name + '_' + name - return getattr(self._conf._namespace, name) - - if name in self._conf: - raise DuplicateOptError(name) - - try: - return getattr(self._conf._namespace, name) - except AttributeError: - raise NoSuchOptError(name) - - class StrSubWrapper(object): - - """Helper class. - - Exposes opt values as a dict for string substitution. - """ - - def __init__(self, conf, group=None, namespace=None): - """Construct a StrSubWrapper object. - - :param conf: a ConfigOpts object - :param group: an OptGroup object - :param namespace: the namespace object that retrieves the option - value from - """ - self.conf = conf - self.group = group - self.namespace = namespace - - def __getitem__(self, key): - """Look up an opt value from the ConfigOpts object. - - :param key: an opt name - :returns: an opt value - """ - try: - group_name, option = key.split(".", 1) - except ValueError: - group = self.group - option = key - else: - group = OptGroup(name=group_name) - try: - value = self.conf._get(option, group=group, - namespace=self.namespace) - except NoSuchOptError: - value = self.conf._get(key, namespace=self.namespace) - if isinstance(value, self.conf.GroupAttr): - raise TemplateSubstitutionError( - 'substituting group %s not supported' % key) - return value - - -CONF = ConfigOpts() diff --git a/oslo_config/cfgfilter.py b/oslo_config/cfgfilter.py deleted file mode 100644 index 77c7775..0000000 --- a/oslo_config/cfgfilter.py +++ /dev/null @@ -1,383 +0,0 @@ -# Copyright 2014 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -r""" -There are three use cases for the ConfigFilter class: - -1. Help enforce that a given module does not access options registered - by another module, without first declaring those cross-module - dependencies using import_opt(). - -2. Prevent private configuration opts from being visible to modules - other than the one which registered it. - -3. Limit the options on a Cfg object that can be accessed. - -.. versionadded:: 1.4 - -Cross-Module Option Dependencies --------------------------------- - -When using the global cfg.CONF object, it is quite common for a module -to require the existence of configuration options registered by other -modules. - -For example, if module 'foo' registers the 'blaa' option and the module -'bar' uses the 'blaa' option then 'bar' might do:: - - import foo - - print(CONF.blaa) - -However, it's completely non-obvious why foo is being imported (is it -unused, can we remove the import) and where the 'blaa' option comes from. - -The CONF.import_opt() method allows such a dependency to be explicitly -declared:: - - CONF.import_opt('blaa', 'foo') - print(CONF.blaa) - -However, import_opt() has a weakness - if 'bar' imports 'foo' using the -import builtin and doesn't use import_opt() to import 'blaa', then 'blaa' -can still be used without problems. Similarly, where multiple options -are registered a module imported via importopt(), a lazy programmer can -get away with only declaring a dependency on a single option. - -The ConfigFilter class provides a way to ensure that options are not -available unless they have been registered in the module or imported using -import_opt() for example with:: - - CONF = ConfigFilter(cfg.CONF) - CONF.import_opt('blaa', 'foo') - print(CONF.blaa) - -no other options other than 'blaa' are available via CONF. - -Private Configuration Options ------------------------------ - -Libraries which register configuration options typically do not want -users of the library API to access those configuration options. If -API users do access private configuration options, those users will -be disrupted if and when a configuration option is renamed. In other -words, one does not typically wish for the name of the private config -options to be part of the public API. - -The ConfigFilter class provides a way for a library to register -options such that they are not visible via the ConfigOpts instance -which the API user supplies to the library. For example:: - - from __future__ import print_function - - from oslo_config.cfg import * - from oslo_config.cfgfilter import * - - class Widget(object): - - def __init__(self, conf): - self.conf = conf - self._private_conf = ConfigFilter(self.conf) - self._private_conf.register_opt(StrOpt('foo')) - - @property - def foo(self): - return self._private_conf.foo - - conf = ConfigOpts() - widget = Widget(conf) - print(widget.foo) - print(conf.foo) # raises NoSuchOptError - - -Limited Configuration Options ------------------------------ - -It may be required that when passing a CONF object to other functions we want -to filter that the receiving code is only able to access a restricted subset -of the options that are available on the CONF object. This is essentially a -more general case of the Private Configuration Options and Cross-Module Options -whereby we expose an option that is already present on the underlying CONF -object without providing any means to load it if not present. - -So given a CONF object with options defined:: - - CONF.register_opt(StrOpt('foo')) - CONF.register_opt(StrOpt('bar')) - -we can expose options such that only those options are present:: - - restricted_conf = CfgFilter(CONF) - restricted_conf.expose_opt('foo') - - print(restricted_conf.foo) - print(restricted_conf.bar) # raises NoSuchOptError -""" - -import collections -import itertools - -from oslo_config import cfg - - -class CliOptRegisteredError(cfg.Error): - """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" \ - " original ConfigOpts." - - if self.msg: - ret += ": " + self.msg - return ret - - -class ConfigFilter(collections.Mapping): - """A helper class which wraps a ConfigOpts object. - - ConfigFilter enforces the explicit declaration of dependencies on external - options and allows private options which are not registered with the - wrapped Configopts object. - """ - - def __init__(self, conf): - """Construct a ConfigFilter object. - - :param conf: a ConfigOpts object - """ - self._conf = conf - self._fconf = cfg.ConfigOpts() - self._sync() - - self._imported_opts = set() - self._imported_groups = dict() - - def _sync(self): - if self._fconf._namespace is not self._conf._namespace: - self._fconf.clear() - self._fconf._namespace = self._conf._namespace - self._fconf._args = self._conf._args - - def __getattr__(self, name): - """Look up an option value. - - :param name: the opt name (or 'dest', more precisely) - :returns: the option value (after string subsititution) or a GroupAttr - :raises: NoSuchOptError,ConfigFileValueError,TemplateSubstitutionError - """ - if name in self._imported_groups: - return self._imported_groups[name] - elif name in self._imported_opts: - return getattr(self._conf, name) - else: - self._sync() - return getattr(self._fconf, name) - - def __getitem__(self, key): - """Look up an option value.""" - return getattr(self, key) - - def __contains__(self, key): - """Return True if key is the name of a registered opt or group.""" - return (key in self._fconf or - key in self._imported_opts or - key in self._imported_groups) - - def __iter__(self): - """Iterate over all registered opt and group names.""" - return itertools.chain(self._fconf.keys(), - self._imported_opts, - self._imported_groups.keys()) - - def __len__(self): - """Return the number of options and option groups.""" - return (len(self._fconf) + - len(self._imported_opts) + - len(self._imported_groups)) - - @staticmethod - def _already_registered(conf, opt, group=None): - group_name = group.name if isinstance(group, cfg.OptGroup) else group - return ((group_name is None and - opt.dest in conf) or - (group_name is not None and - group_name in conf and - opt.dest in conf[group_name])) - - def register_opt(self, opt, group=None): - """Register an option schema. - - :param opt: an instance of an Opt sub-class - :param group: an optional OptGroup object or group name - :return: False if the opt was already registered, True otherwise - :raises: DuplicateOptError - """ - if self._already_registered(self._conf, opt, group): - # Raises DuplicateError if there is another opt with the same name - ret = self._conf.register_opt(opt, group) - self._import_opt(opt.dest, group) - return ret - else: - return self._fconf.register_opt(opt, group) - - def register_opts(self, opts, group=None): - """Register multiple option schemas at once.""" - for opt in opts: - self.register_opt(opt, group) - - def register_cli_opt(self, opt, group=None): - """Register a CLI option schema. - - :param opt: an instance of an Opt sub-class - :param group: an optional OptGroup object or group name - :return: False if the opt was already register, True otherwise - :raises: DuplicateOptError, ArgsAlreadyParsedError - """ - if self._already_registered(self._conf, opt, group): - # Raises DuplicateError if there is another opt with the same name - ret = self._conf.register_opt( - opt, group, cli=True, clear_cache=False) - self._import_opt(opt.dest, group) - return ret - else: - raise CliOptRegisteredError( - "Opt '{0}' cannot be registered.".format(opt.dest)) - - def register_cli_opts(self, opts, group=None): - """Register multiple CLI option schemas at once.""" - for opt in opts: - self.register_cli_opt(opt, group) - - def register_group(self, group): - """Register an option group. - - :param group: an OptGroup object - """ - self._fconf.register_group(group) - - def import_opt(self, opt_name, module_str, group=None): - """Import an option definition from a module. - - :param name: the name/dest of the opt - :param module_str: the name of a module to import - :param group: an option OptGroup object or group name - :raises: NoSuchOptError, NoSuchGroupError - """ - self._conf.import_opt(opt_name, module_str, group) - self._import_opt(opt_name, group) - - def import_group(self, group, module_str): - """Import an option group from a module. - - Note that this allows access to all options registered with - the group whether or not those options were registered by - the given module. - - :param group: an option OptGroup object or group name - :param module_str: the name of a module to import - :raises: ImportError, NoSuchGroupError - """ - self._conf.import_group(group, module_str) - group = self._import_group(group) - group._all_opts = True - - def _import_opt(self, opt_name, group): - if group is None: - self._imported_opts.add(opt_name) - return True - else: - group = self._import_group(group) - return group._import_opt(opt_name) - - def _import_group(self, group_or_name): - if isinstance(group_or_name, cfg.OptGroup): - group_name = group_or_name.name - else: - group_name = group_or_name - - if group_name in self._imported_groups: - return self._imported_groups[group_name] - else: - group = self.GroupAttr(self._conf, group_name) - self._imported_groups[group_name] = group - return group - - def expose_opt(self, opt_name, group=None): - """Expose an option from the underlying conf object. - - This allows an object that has already been imported or used from the - base conf object to be seen from the filter object. - - :param opt_name: the name/dest of the opt - :param group: an option OptGroup object or group name - """ - self._import_opt(opt_name, group) - - def expose_group(self, group): - """Expose all option from a group in the underlying conf object. - - This allows an object that has already been imported or used from the - base conf object to be seen from the filter object. - - :param group: an option OptGroup object or group name - """ - group = self._import_group(group) - group._all_opts = True - - class GroupAttr(collections.Mapping): - - """Helper class to wrap a group object. - - Represents the option values of a group as a mapping and attributes. - """ - - def __init__(self, conf, group): - """Construct a GroupAttr object. - - :param conf: a ConfigOpts object - :param group: an OptGroup object - """ - self._conf = conf - self._group = group - self._imported_opts = set() - self._all_opts = False - - def __getattr__(self, name): - """Look up an option value.""" - if not self._all_opts and name not in self._imported_opts: - raise cfg.NoSuchOptError(name) - return getattr(self._conf[self._group], name) - - def __getitem__(self, key): - """Look up an option value.""" - return getattr(self, key) - - def __contains__(self, key): - """Return True if key is the name of a registered opt or group.""" - return key in self._imported_opts - - def __iter__(self): - """Iterate over all registered opt and group names.""" - for key in self._imported_opts: - yield key - - def __len__(self): - """Return the number of options and option groups.""" - return len(self._imported_opts) - - def _import_opt(self, opt_name): - self._imported_opts.add(opt_name) diff --git a/oslo_config/fixture.py b/oslo_config/fixture.py deleted file mode 100644 index 6f047e8..0000000 --- a/oslo_config/fixture.py +++ /dev/null @@ -1,206 +0,0 @@ -# -# Copyright 2013 Mirantis, Inc. -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import fixtures -import six - -from oslo_config import cfg - - -class Config(fixtures.Fixture): - """Allows overriding configuration settings for the test. - - `conf` will be reset on cleanup. - - """ - - def __init__(self, conf=cfg.CONF): - self.conf = conf - - def setUp(self): - super(Config, self).setUp() - # NOTE(morganfainberg): unregister must be added to cleanup before - # reset is because cleanup works in reverse order of registered items, - # and a reset must occur before unregistering options can occur. - self.addCleanup(self._reset_default_config_files) - self.addCleanup(self._unregister_config_opts) - self.addCleanup(self.conf.reset) - self._registered_config_opts = {} - - # Grab an old copy of the default config files - if it exists - for - # subsequent cleanup. - if hasattr(self.conf, 'default_config_files'): - self._default_config_files = self.conf.default_config_files - else: - self._default_config_files = None - - def config(self, **kw): - """Override configuration values. - - The keyword arguments are the names of configuration options to - override and their values. - - If a `group` argument is supplied, the overrides are applied to - the specified configuration option group, otherwise the overrides - are applied to the ``default`` group. - - If a `enforce_type` is supplied, will convert the override - value to the option's type before overriding. - """ - - group = kw.pop('group', None) - enforce_type = kw.pop('enforce_type', False) - for k, v in six.iteritems(kw): - self.conf.set_override(k, v, group, enforce_type=enforce_type) - - def _unregister_config_opts(self): - for group in self._registered_config_opts: - self.conf.unregister_opts(self._registered_config_opts[group], - group=group) - - def _reset_default_config_files(self): - if not hasattr(self.conf, 'default_config_files'): - return - - if self._default_config_files: - self.conf.default_config_files = self._default_config_files - else: - # Delete, because we could conceivably begin with the property - # being unset. - self.conf.default_config_files = None - - def register_opt(self, opt, group=None): - """Register a single option for the test run. - - Options registered in this manner will automatically be unregistered - during cleanup. - - If a `group` argument is supplied, it will register the new option - to that group, otherwise the option is registered to the ``default`` - group. - """ - self.conf.register_opt(opt, group=group) - self._registered_config_opts.setdefault(group, set()).add(opt) - - def register_opts(self, opts, group=None): - """Register multiple options for the test run. - - This works in the same manner as register_opt() but takes a list of - options as the first argument. All arguments will be registered to the - same group if the ``group`` argument is supplied, otherwise all options - will be registered to the ``default`` group. - """ - for opt in opts: - self.register_opt(opt, group=group) - - def register_cli_opt(self, opt, group=None): - """Register a single CLI option for the test run. - - Options registered in this manner will automatically be unregistered - during cleanup. - - If a `group` argument is supplied, it will register the new option - to that group, otherwise the option is registered to the ``default`` - group. - - CLI options must be registered before the command line and config files - are parsed. This is to ensure that all CLI options are shown in --help - and option validation works as expected. - """ - self.conf.register_cli_opt(opt, group=group) - self._registered_config_opts.setdefault(group, set()).add(opt) - - def register_cli_opts(self, opts, group=None): - """Register multiple CLI options for the test run. - - This works in the same manner as register_opt() but takes a list of - options as the first argument. All arguments will be registered to the - same group if the ``group`` argument is supplied, otherwise all options - will be registered to the ``default`` group. - - CLI options must be registered before the command line and config files - are parsed. This is to ensure that all CLI options are shown in --help - and option validation works as expected. - """ - for opt in opts: - self.register_cli_opt(opt, group=group) - - def load_raw_values(self, group=None, **kwargs): - """Load raw values into the configuration without registering them. - - This method adds a series of parameters into the current config - instance, as if they had been loaded by a ConfigParser. This method - does not require that you register the configuration options first, - however the values loaded will not be accessible until you do. - """ - - # Make sure the namespace exists for our tests. - if not self.conf._namespace: - self.conf.__call__(args=[]) - - # Default out the group name - group = 'DEFAULT' if not group else group - - raw_config = dict() - raw_config[group] = dict() - for key, value in six.iteritems(kwargs): - # Parsed values are an array of raw strings. - raw_config[group][key] = [str(value)] - - self.conf._namespace._add_parsed_config_file(raw_config, raw_config) - - def set_config_files(self, config_files): - """Specify a list of config files to read. - - This method allows you to predefine the list of configuration files - that are loaded by oslo_config. It will ensure that your tests do not - attempt to autodetect, and accidentally pick up config files from - locally installed services. - """ - if not isinstance(config_files, list): - raise AttributeError("Please pass a list() to set_config_files()") - - # Make sure the namespace exists for our tests. - if not self.conf._namespace: - self.conf.__call__(args=[]) - - self.conf.default_config_files = config_files - self.conf.reload_config_files() - - def set_default(self, name, default, group=None): - """Set a default value for an option. - - This method is not necessarily meant to be invoked - directly. It is here to allow the set_defaults() functions in - various Oslo libraries to work with a Config fixture instead - of a ConfigOpts instance. - - Use it like:: - - class MyTest(testtools.TestCase): - - def setUp(self): - super(MyTest, self).setUp() - self.conf = self.useFixture(fixture.Config()) - - def test_something(self): - some_library.set_defaults(self.conf, name='value') - some_library.do_something_exciting() - - """ - self.conf.set_default(name, default, group) - self.addCleanup(self.conf.clear_default, name, group) diff --git a/oslo_config/generator.py b/oslo_config/generator.py deleted file mode 100644 index 22a9f28..0000000 --- a/oslo_config/generator.py +++ /dev/null @@ -1,483 +0,0 @@ -# Copyright 2012 SINA Corporation -# Copyright 2014 Cisco Systems, Inc. -# All Rights Reserved. -# Copyright 2014 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Sample configuration generator - -Tool for generating a sample configuration file. See -../doc/source/generator.rst for details. - -.. versionadded:: 1.4 -""" - -import collections -import logging -import operator -import sys -import textwrap - -import pkg_resources -import six - -from oslo_config._i18n import _LW -from oslo_config import cfg -import stevedore.named # noqa - -LOG = logging.getLogger(__name__) - -_generator_opts = [ - cfg.StrOpt('output-file', - help='Path of the file to write to. Defaults to stdout.'), - cfg.IntOpt('wrap-width', - default=70, - help='The maximum length of help lines.'), - cfg.MultiStrOpt('namespace', - required=True, - help='Option namespace under "oslo.config.opts" in which ' - 'to query for options.'), - cfg.BoolOpt('minimal', default=False, - help='Generate a minimal required configuration.'), -] - - -def register_cli_opts(conf): - """Register the formatter's CLI options with a ConfigOpts instance. - - Note, this must be done before the ConfigOpts instance is called to parse - the configuration. - - :param conf: a ConfigOpts instance - :raises: DuplicateOptError, ArgsAlreadyParsedError - """ - conf.register_cli_opts(_generator_opts) - - -def _format_defaults(opt): - "Return a list of formatted default values." - if isinstance(opt, cfg.MultiStrOpt): - if opt.sample_default is not None: - defaults = opt.sample_default - elif not opt.default: - defaults = [''] - else: - defaults = opt.default - else: - if opt.sample_default is not None: - default_str = str(opt.sample_default) - elif opt.default is None: - default_str = '' - elif (isinstance(opt, (cfg.StrOpt, cfg.IPOpt, - cfg.HostnameOpt, cfg.URIOpt))): - default_str = opt.default - elif isinstance(opt, cfg.BoolOpt): - default_str = str(opt.default).lower() - elif isinstance(opt, (cfg.IntOpt, cfg.FloatOpt, - cfg.PortOpt)): - default_str = str(opt.default) - elif isinstance(opt, (cfg.ListOpt, cfg._ConfigFileOpt, - cfg._ConfigDirOpt)): - default_str = ','.join(opt.default) - elif isinstance(opt, cfg.DictOpt): - sorted_items = sorted(opt.default.items(), - key=operator.itemgetter(0)) - default_str = ','.join(['%s:%s' % i for i in sorted_items]) - else: - LOG.warning(_LW('Unknown option type: %s'), repr(opt)) - default_str = str(opt.default) - defaults = [default_str] - - results = [] - for default_str in defaults: - if default_str.strip() != default_str: - default_str = '"%s"' % default_str - results.append(default_str) - return results - - -_TYPE_NAMES = { - str: 'string value', - int: 'integer value', - float: 'floating point value', -} - - -def _format_type_name(opt_type): - """Format the type name to use in describing an option""" - try: - return opt_type.type_name - except AttributeError: # nosec - pass - - try: - return _TYPE_NAMES[opt_type] - except KeyError: # nosec - pass - - return 'unknown value' - - -class _OptFormatter(object): - - """Format configuration option descriptions to a file.""" - - def __init__(self, output_file=None, wrap_width=70): - """Construct an OptFormatter object. - - :param output_file: a writeable file object - :param wrap_width: The maximum length of help lines, 0 to not wrap - """ - self.output_file = output_file or sys.stdout - self.wrap_width = wrap_width - - def _format_help(self, help_text): - """Format the help for a group or option to the output file. - - :param help_text: The text of the help string - """ - if self.wrap_width is not None and self.wrap_width > 0: - wrapped = "" - for line in help_text.splitlines(): - text = "\n".join(textwrap.wrap(line, self.wrap_width, - initial_indent='# ', - subsequent_indent='# ', - break_long_words=False, - replace_whitespace=False)) - wrapped += "#" if text == "" else text - wrapped += "\n" - lines = [wrapped] - else: - lines = ['# ' + help_text + '\n'] - return lines - - def _get_choice_text(self, choice): - if choice is None: - return '' - elif choice == '': - return "''" - return six.text_type(choice) - - def format_group(self, group_or_groupname): - """Format the description of a group header to the output file - - :param group_or_groupname: a cfg.OptGroup instance or a name of group - """ - if isinstance(group_or_groupname, cfg.OptGroup): - group = group_or_groupname - lines = ['[%s]\n' % group.name] - if group.help: - lines += self._format_help(group.help) - else: - groupname = group_or_groupname - lines = ['[%s]\n' % groupname] - self.writelines(lines) - - def format(self, opt, group_name, minimal=False): - """Format a description of an option to the output file. - - :param opt: a cfg.Opt instance - """ - if not opt.help: - LOG.warning(_LW('"%s" is missing a help string'), opt.dest) - - opt_type = _format_type_name(opt.type) - opt_prefix = '' - if (opt.deprecated_for_removal and - not opt.help.startswith('DEPRECATED')): - opt_prefix = 'DEPRECATED: ' - - if opt.help: - help_text = u'%s%s (%s)' % (opt_prefix, - opt.help, - opt_type) - else: - help_text = u'(%s)' % opt_type - lines = self._format_help(help_text) - - if getattr(opt.type, 'min', None) is not None: - lines.append('# Minimum value: %d\n' % opt.type.min) - - if getattr(opt.type, 'max', None) is not None: - lines.append('# Maximum value: %d\n' % opt.type.max) - - if getattr(opt.type, 'choices', None): - choices_text = ', '.join([self._get_choice_text(choice) - for choice in opt.type.choices]) - lines.append('# Allowed values: %s\n' % choices_text) - - try: - if opt.mutable: - lines.append( - '# Note: This option can be changed without restarting.\n' - ) - except AttributeError as err: - # NOTE(dhellmann): keystoneauth defines its own Opt class, - # and neutron (at least) returns instances of those - # classes instead of oslo_config Opt instances. The new - # mutable attribute is the first property where the API - # isn't supported in the external class, so we can use - # this failure to emit a warning. See - # https://bugs.launchpad.net/keystoneauth/+bug/1548433 for - # more details. - import warnings - if not isinstance(opt, cfg.Opt): - warnings.warn( - 'Incompatible option class for %s (%r): %s' % - (opt.dest, opt.__class__, err), - ) - else: - warnings.warn('Failed to fully format sample for %s: %s' % - (opt.dest, err)) - - for d in opt.deprecated_opts: - lines.append('# Deprecated group/name - [%s]/%s\n' % - (d.group or group_name, d.name or opt.dest)) - - if opt.deprecated_for_removal: - if opt.deprecated_since: - lines.append( - '# This option is deprecated for removal since %s.\n' % ( - opt.deprecated_since)) - else: - lines.append( - '# This option is deprecated for removal.\n') - lines.append( - '# Its value may be silently ignored in the future.\n') - if opt.deprecated_reason: - lines.extend( - self._format_help('Reason: ' + opt.deprecated_reason)) - - if opt.advanced: - lines.append( - '# Advanced Option: intended for advanced users and not used\n' - '# by the majority of users, and might have a significant\n' - '# effect on stability and/or performance.\n' - ) - - if hasattr(opt.type, 'format_defaults'): - defaults = opt.type.format_defaults(opt.default, - opt.sample_default) - else: - LOG.debug( - "The type for option %(name)s which is %(type)s is not a " - "subclass of types.ConfigType and doesn't provide a " - "'format_defaults' method. A default formatter is not " - "available so the best-effort formatter will be used.", - {'type': opt.type, 'name': opt.name}) - defaults = _format_defaults(opt) - for default_str in defaults: - if default_str: - default_str = ' ' + default_str - if minimal: - lines.append('%s =%s\n' % (opt.dest, default_str)) - else: - lines.append('#%s =%s\n' % (opt.dest, default_str)) - - self.writelines(lines) - - def write(self, s): - """Write an arbitrary string to the output file. - - :param s: an arbitrary string - """ - self.output_file.write(s) - - def writelines(self, l): - """Write an arbitrary sequence of strings to the output file. - - :param l: a list of arbitrary strings - """ - self.output_file.writelines(l) - - -def _cleanup_opts(read_opts): - """Cleanup duplicate options in namespace groups - - Return a structure which removes duplicate options from a namespace group. - NOTE:(rbradfor) This does not remove duplicated options from repeating - groups in different namespaces: - - :param read_opts: a list (namespace, [(group, [opt_1, opt_2])]) tuples - :returns: a list of (namespace, [(group, [opt_1, opt_2])]) tuples - """ - - # OrderedDict is used specifically in the three levels to maintain the - # source order of namespace/group/opt values - clean = collections.OrderedDict() - for namespace, listing in read_opts: - if namespace not in clean: - clean[namespace] = collections.OrderedDict() - for group, opts in listing: - if group not in clean[namespace]: - clean[namespace][group] = collections.OrderedDict() - for opt in opts: - clean[namespace][group][opt.dest] = opt - - # recreate the list of (namespace, [(group, [opt_1, opt_2])]) tuples - # from the cleaned structure. - cleaned_opts = [ - (namespace, [(group, list(clean[namespace][group].values())) - for group in clean[namespace]]) - for namespace in clean - ] - - return cleaned_opts - - -def _get_raw_opts_loaders(namespaces): - """List the options available via the given namespaces. - - :param namespaces: a list of namespaces registered under 'oslo.config.opts' - :returns: a list of (namespace, [(group, [opt_1, opt_2])]) tuples - """ - mgr = stevedore.named.NamedExtensionManager( - 'oslo.config.opts', - names=namespaces, - on_load_failure_callback=on_load_failure_callback, - invoke_on_load=False) - return [(e.name, e.plugin) for e in mgr] - - -def _get_opt_default_updaters(namespaces): - mgr = stevedore.named.NamedExtensionManager( - 'oslo.config.opts.defaults', - names=namespaces, - on_load_failure_callback=on_load_failure_callback, - invoke_on_load=False) - return [ep.plugin for ep in mgr] - - -def _update_defaults(namespaces): - "Let application hooks update defaults inside libraries." - for update in _get_opt_default_updaters(namespaces): - update() - - -def _list_opts(namespaces): - """List the options available via the given namespaces. - - Duplicate options from a namespace are removed. - - :param namespaces: a list of namespaces registered under 'oslo.config.opts' - :returns: a list of (namespace, [(group, [opt_1, opt_2])]) tuples - """ - # Load the functions to get the options. - loaders = _get_raw_opts_loaders(namespaces) - # Update defaults, which might change global settings in library - # modules. - _update_defaults(namespaces) - # Ask for the option definitions. At this point any global default - # changes made by the updaters should be in effect. - opts = [ - (namespace, loader()) - for namespace, loader in loaders - ] - return _cleanup_opts(opts) - - -def on_load_failure_callback(*args, **kwargs): - raise - - -def _output_opts(f, group, group_data, minimal=False): - f.format_group(group_data['object'] or group) - for (namespace, opts) in sorted(group_data['namespaces'], - key=operator.itemgetter(0)): - f.write('\n#\n# From %s\n#\n' % namespace) - for opt in sorted(opts, key=operator.attrgetter('advanced')): - try: - if minimal and not opt.required: - pass - else: - f.write('\n') - f.format(opt, group, minimal) - except Exception as err: - f.write('# Warning: Failed to format sample for %s\n' % - (opt.dest,)) - f.write('# %s\n' % (err,)) - - -def _get_groups(conf_ns): - """Invert a list of groups by namespace into a dict by group name. - - :param conf_ns: a list of (namespace, [(, [opt_1, opt_2])]) tuples, - such as returned by _list_opts. - :returns: {, {'object': , - 'namespaces': [(, )]}} - - may be a string or a group object. - is always a string. - will only be set if was a group object in at least - one namespace. - - Keying by group_name avoids adding duplicate group names in case a group is - added as both an OptGroup and as a str, but still makes the additional - OptGroup data available to the output code when possible. - """ - groups = {'DEFAULT': {'object': None, 'namespaces': []}} - for namespace, listing in conf_ns: - for group, opts in listing: - if not opts: - continue - group = group if group else 'DEFAULT' - is_optgroup = hasattr(group, 'name') - group_name = group.name if is_optgroup else group - if group_name not in groups: - groups[group_name] = {'object': None, 'namespaces': []} - if is_optgroup: - groups[group_name]['object'] = group - groups[group_name]['namespaces'].append((namespace, opts)) - return groups - - -def generate(conf): - """Generate a sample config file. - - List all of the options available via the namespaces specified in the given - configuration and write a description of them to the specified output file. - - :param conf: a ConfigOpts instance containing the generator's configuration - """ - conf.register_opts(_generator_opts) - - output_file = (open(conf.output_file, 'w') - if conf.output_file else sys.stdout) - - formatter = _OptFormatter(output_file=output_file, - wrap_width=conf.wrap_width) - - groups = _get_groups(_list_opts(conf.namespace)) - - # Output the "DEFAULT" section as the very first section - _output_opts(formatter, 'DEFAULT', groups.pop('DEFAULT'), conf.minimal) - - # output all other config sections with groups in alphabetical order - for group, group_data in sorted(groups.items()): - formatter.write('\n\n') - _output_opts(formatter, group, group_data, conf.minimal) - - -def main(args=None): - """The main function of oslo-config-generator.""" - version = pkg_resources.get_distribution('oslo.config').version - logging.basicConfig(level=logging.WARN) - conf = cfg.ConfigOpts() - register_cli_opts(conf) - conf(args, version=version) - generate(conf) - - -if __name__ == '__main__': - main() diff --git a/oslo_config/iniparser.py b/oslo_config/iniparser.py deleted file mode 100644 index 90218e4..0000000 --- a/oslo_config/iniparser.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -class ParseError(Exception): - def __init__(self, message, lineno, line): - self.msg = message - self.line = line - self.lineno = lineno - - def __str__(self): - return 'at line %d, %s: %r' % (self.lineno, self.msg, self.line) - - -class BaseParser(object): - lineno = 0 - parse_exc = ParseError - - def _assignment(self, key, value): - self.assignment(key, value) - return None, [] - - def _get_section(self, line): - if not line.endswith(']'): - return self.error_no_section_end_bracket(line) - if len(line) <= 2: - return self.error_no_section_name(line) - - return line[1:-1] - - def _split_key_value(self, line): - colon = line.find(':') - equal = line.find('=') - if colon < 0 and equal < 0: - return self.error_invalid_assignment(line) - - if colon < 0 or (equal >= 0 and equal < colon): - key, value = line[:equal], line[equal + 1:] - else: - key, value = line[:colon], line[colon + 1:] - - value = value.strip() - if value and value[0] == value[-1] and value.startswith(("\"", "'")): - value = value[1:-1] - return key.strip(), [value] - - def parse(self, lineiter): - key = None - value = [] - - for line in lineiter: - self.lineno += 1 - - line = line.rstrip() - if not line: - # Blank line, ends multi-line values - if key: - key, value = self._assignment(key, value) - continue - elif line.startswith((' ', '\t')): - # Continuation of previous assignment - if key is None: - self.error_unexpected_continuation(line) - else: - value.append(line.lstrip()) - continue - - if key: - # Flush previous assignment, if any - key, value = self._assignment(key, value) - - if line.startswith('['): - # Section start - section = self._get_section(line) - if section: - self.new_section(section) - elif line.startswith(('#', ';')): - self.comment(line[1:].lstrip()) - else: - key, value = self._split_key_value(line) - if not key: - return self.error_empty_key(line) - - if key: - # Flush previous assignment, if any - self._assignment(key, value) - - def assignment(self, key, value): - """Called when a full assignment is parsed.""" - raise NotImplementedError() - - def new_section(self, section): - """Called when a new section is started.""" - raise NotImplementedError() - - def comment(self, comment): - """Called when a comment is parsed.""" - pass - - def error_invalid_assignment(self, line): - raise self.parse_exc("No ':' or '=' found in assignment", - self.lineno, line) - - def error_empty_key(self, line): - raise self.parse_exc('Key cannot be empty', self.lineno, line) - - def error_unexpected_continuation(self, line): - raise self.parse_exc('Unexpected continuation line', - self.lineno, line) - - def error_no_section_end_bracket(self, line): - raise self.parse_exc('Invalid section (must end with ])', - self.lineno, line) - - def error_no_section_name(self, line): - raise self.parse_exc('Empty section name', self.lineno, line) diff --git a/oslo_config/sphinxconfiggen.py b/oslo_config/sphinxconfiggen.py deleted file mode 100644 index 9c3cdbf..0000000 --- a/oslo_config/sphinxconfiggen.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2015 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import os - -from oslo_config import generator - - -def generate_sample(app): - - if not app.config.config_generator_config_file: - app.warn("No config_generator_config_file is specified, " - "skipping sample config generation") - return - - # Decided to update the existing config option - # config_generator_config_file to support a value that is a list of - # tuples, containing the file names as (input, output). - # We need to retain support for the option referring to a single string, - # and using the sample_config_basename for the output file in that case. - # After we release support for both forms of the option, we can update - # projects to always use the list of tuples, then remove - # sample_config_basename and the support for config_generator_config_file - # being a single string. - - if isinstance(app.config.config_generator_config_file, list): - for config_file, base_name in app.config.config_generator_config_file: - if base_name is None: - base_name = _get_default_basename(config_file) - _generate_sample(app, config_file, base_name) - else: - _generate_sample(app, - app.config.config_generator_config_file, - app.config.sample_config_basename) - - -def _get_default_basename(config_file): - return os.path.splitext(os.path.basename(config_file))[0] - - -def _generate_sample(app, config_file, base_name): - - def info(msg): - app.info('[%s] %s' % (__name__, msg)) - - # If we are given a file that isn't an absolute path, look for it - # in the source directory if it doesn't exist. - candidates = [ - config_file, - os.path.join(app.srcdir, config_file,), - ] - for c in candidates: - if os.path.isfile(c): - info('reading config generator instructions from %s' % c) - config_path = c - break - else: - raise ValueError( - "Could not find config_generator_config_file %r" % - app.config.config_generator_config_file) - - if base_name: - out_file = os.path.join(app.srcdir, base_name) + '.conf.sample' - if not os.path.isdir(os.path.dirname(os.path.abspath(out_file))): - os.mkdir(os.path.dirname(os.path.abspath(out_file))) - else: - file_name = 'sample.config' - out_file = os.path.join(app.srcdir, file_name) - - info('writing sample configuration to %s' % out_file) - generator.main(args=['--config-file', config_path, - '--output-file', out_file]) - - -def setup(app): - app.add_config_value('config_generator_config_file', None, 'env') - app.add_config_value('sample_config_basename', None, 'env') - app.connect('builder-inited', generate_sample) diff --git a/oslo_config/sphinxext.py b/oslo_config/sphinxext.py deleted file mode 100644 index 283f630..0000000 --- a/oslo_config/sphinxext.py +++ /dev/null @@ -1,453 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from docutils import nodes -from docutils.parsers import rst -from docutils.parsers.rst import directives - -from docutils.statemachine import ViewList -from sphinx import addnodes -from sphinx.directives import ObjectDescription -from sphinx.domains import Domain -from sphinx.domains import ObjType -from sphinx.roles import XRefRole -from sphinx.util.nodes import make_refnode -from sphinx.util.nodes import nested_parse_with_titles - -from oslo_config import cfg -from oslo_config import generator -import oslo_i18n - -import six - - -def _list_table(headers, data, title='', columns=None): - """Build a list-table directive. - - :param add: Function to add one row to output. - :param headers: List of header values. - :param data: Iterable of row data, yielding lists or tuples with rows. - """ - yield '.. list-table:: %s' % title - yield ' :header-rows: 1' - if columns: - yield ' :widths: %s' % (','.join(str(c) for c in columns)) - yield '' - yield ' - * %s' % headers[0] - for h in headers[1:]: - yield ' * %s' % h - for row in data: - yield ' - * %s' % row[0] - for r in row[1:]: - yield ' * %s' % r - yield '' - - -def _indent(text, n=2): - padding = ' ' * n - return '\n'.join(padding + l for l in text.splitlines()) - - -def _make_anchor_target(group_name, option_name): - # We need to ensure this is unique across entire documentation - # http://www.sphinx-doc.org/en/stable/markup/inline.html#ref-role - target = '%s.%s' % (cfg._normalize_group_name(group_name), - option_name.lower()) - return target - - -_TYPE_DESCRIPTIONS = { - cfg.StrOpt: 'string', - cfg.BoolOpt: 'boolean', - cfg.IntOpt: 'integer', - cfg.FloatOpt: 'floating point', - cfg.ListOpt: 'list', - cfg.DictOpt: 'dict', - cfg.MultiStrOpt: 'multi-valued', - cfg._ConfigFileOpt: 'list of filenames', - cfg._ConfigDirOpt: 'list of directory names', -} - - -def _get_choice_text(choice): - if choice is None: - return '' - elif choice == '': - return "''" - return six.text_type(choice) - - -def _format_group(app, namespace, group_name, group_obj, opt_list): - group_name = group_name or 'DEFAULT' - app.info('[oslo.config] %s %s' % (namespace, group_name)) - - yield '.. oslo.config:group:: %s' % group_name - if namespace: - yield ' :namespace: %s' % namespace - yield '' - - if group_obj and group_obj.help: - yield _indent(group_obj.help.rstrip()) - yield '' - - for opt in opt_list: - opt_type = _TYPE_DESCRIPTIONS.get(type(opt), - 'unknown type') - yield '.. oslo.config:option:: %s' % opt.dest - yield '' - yield _indent(':Type: %s' % opt_type) - for default in generator._format_defaults(opt): - if default: - default = '``' + default + '``' - yield _indent(':Default: %s' % default) - if getattr(opt.type, 'min', None) is not None: - yield _indent(':Minimum Value: %s' % opt.type.min) - if getattr(opt.type, 'max', None) is not None: - yield _indent(':Maximum Value: %s' % opt.type.max) - if getattr(opt.type, 'choices', None): - choices_text = ', '.join([_get_choice_text(choice) - for choice in opt.type.choices]) - yield _indent(':Valid Values: %s' % choices_text) - try: - if opt.mutable: - yield _indent( - ':Mutable: This option can be changed without restarting.', - ) - except AttributeError as err: - # NOTE(dhellmann): keystoneauth defines its own Opt class, - # and neutron (at least) returns instances of those - # classes instead of oslo_config Opt instances. The new - # mutable attribute is the first property where the API - # isn't supported in the external class, so we can use - # this failure to emit a warning. See - # https://bugs.launchpad.net/keystoneauth/+bug/1548433 for - # more details. - import warnings - if not isinstance(cfg.Opt, opt): - warnings.warn( - 'Incompatible option class for %s (%r): %s' % - (opt.dest, opt.__class__, err), - ) - else: - warnings.warn('Failed to fully format sample for %s: %s' % - (opt.dest, err)) - if opt.advanced: - yield _indent( - ':Advanced Option: intended for advanced users and not used',) - yield _indent( - ':by the majority of users, and might have a significant',) - yield _indent( - ':effect on stability and/or performance.',) - yield '' - - try: - help_text = opt.help % {'default': 'the value above'} - except (TypeError, KeyError): - # There is no mention of the default in the help string, - # or the string had some unknown key - help_text = opt.help - if help_text: - yield _indent(help_text) - yield '' - - if opt.deprecated_opts: - for line in _list_table( - ['Group', 'Name'], - ((d.group or group_name, - d.name or opt.dest or 'UNSET') - for d in opt.deprecated_opts), - title='Deprecated Variations'): - yield _indent(line) - if opt.deprecated_for_removal: - yield _indent('.. warning::') - if opt.deprecated_since: - yield _indent(' This option is deprecated for removal ' - 'since %s.' % opt.deprecated_since) - else: - yield _indent(' This option is deprecated for removal.') - yield _indent(' Its value may be silently ignored ') - yield _indent(' in the future.') - yield '' - if opt.deprecated_reason: - yield _indent(' :Reason: ' + opt.deprecated_reason) - yield '' - - yield '' - - -def _format_option_help(app, namespaces, split_namespaces): - """Generate a series of lines of restructuredtext. - - Format the option help as restructuredtext and return it as a list - of lines. - """ - opts = generator._list_opts(namespaces) - - if split_namespaces: - for namespace, opt_list in opts: - for group, opts in opt_list: - if isinstance(group, cfg.OptGroup): - group_name = group.name - else: - group_name = group - group = None - lines = _format_group( - app=app, - namespace=namespace, - group_name=group_name, - group_obj=group, - opt_list=opts, - ) - for line in lines: - yield line - else: - # Merge the options from different namespaces that belong to - # the same group together and format them without the - # namespace. - by_section = {} - group_objs = {} - for ignore, opt_list in opts: - for group, group_opts in opt_list: - if isinstance(group, cfg.OptGroup): - group_name = group.name - else: - group_name = group - group = None - group_objs.setdefault(group_name, group) - by_section.setdefault(group_name, []).extend(group_opts) - for group_name, group_opts in sorted(by_section.items()): - lines = _format_group( - app=app, - namespace=None, - group_name=group_name, - group_obj=group_objs.get(group_name), - opt_list=group_opts, - ) - for line in lines: - yield line - - -class ShowOptionsDirective(rst.Directive): - - option_spec = { - 'split-namespaces': directives.flag, - 'config-file': directives.unchanged, - } - - has_content = True - - def run(self): - env = self.state.document.settings.env - app = env.app - - split_namespaces = 'split-namespaces' in self.options - - config_file = self.options.get('config-file') - if config_file: - app.info('loading config file %s' % config_file) - conf = cfg.ConfigOpts() - conf.register_opts(generator._generator_opts) - conf( - args=['--config-file', config_file], - project='oslo.config.sphinxext', - ) - namespaces = conf.namespace[:] - else: - namespaces = [ - c.strip() - for c in self.content - if c.strip() - ] - - result = ViewList() - source_name = '<' + __name__ + '>' - for line in _format_option_help(app, namespaces, split_namespaces): - result.append(line, source_name) - - node = nodes.section() - node.document = self.state.document - nested_parse_with_titles(self.state, result, node) - - return node.children - - -class ConfigGroupXRefRole(XRefRole): - "Handles :oslo.config:group: roles pointing to configuration groups." - - def __init__(self): - super(ConfigGroupXRefRole, self).__init__( - warn_dangling=True, - ) - - def process_link(self, env, refnode, has_explicit_title, title, target): - # The anchor for the group link is the group name. - return target, target - - -class ConfigOptXRefRole(XRefRole): - "Handles :oslo.config:option: roles pointing to configuration options." - - def __init__(self): - super(ConfigOptXRefRole, self).__init__( - warn_dangling=True, - ) - - def process_link(self, env, refnode, has_explicit_title, title, target): - if not has_explicit_title: - title = target - if '.' in target: - group, opt_name = target.split('.') - else: - group = 'DEFAULT' - opt_name = target - anchor = _make_anchor_target(group, opt_name) - return title, anchor - - -class ConfigGroup(rst.Directive): - - has_content = True - - option_spec = { - 'namespace': directives.unchanged, - } - - def run(self): - env = self.state.document.settings.env - app = env.app - - group_name = ' '.join(self.content) - namespace = self.options.get('namespace') - - cached_groups = env.domaindata['oslo.config']['groups'] - - # Store the current group for use later in option directives - env.temp_data['oslo.config:group'] = group_name - app.info('oslo.config group %r' % group_name) - - # Store the location where this group is being defined - # for use when resolving cross-references later. - # FIXME: This should take the source namespace into account, too - cached_groups[group_name] = env.docname - - result = ViewList() - source_name = '<' + __name__ + '>' - - def _add(text): - "Append some text to the output result view to be parsed." - result.append(text, source_name) - - if namespace: - title = '%s: %s' % (namespace, group_name) - else: - title = group_name - - _add(title) - _add('-' * len(title)) - node = nodes.section() - node.document = self.state.document - nested_parse_with_titles(self.state, result, node) - - first_child = node.children[0] - - # Compute the normalized target and set the node to have that - # as an id - target_name = cfg._normalize_group_name(group_name) - first_child['ids'].append(target_name) - - indexnode = addnodes.index(entries=[]) - return [indexnode] + node.children - - -class ConfigOption(ObjectDescription): - "Description of a configuration option (.. option)." - - def handle_signature(self, sig, signode): - """Transform an option description into RST nodes.""" - optname = sig - self.env.app.info('oslo.config option %s' % optname) - # Insert a node into the output showing the option name - signode += addnodes.desc_name(optname, optname) - signode['allnames'] = [optname] - return optname - - def add_target_and_index(self, firstname, sig, signode): - cached_options = self.env.domaindata['oslo.config']['options'] - # Look up the current group name from the processing context - currgroup = self.env.temp_data.get('oslo.config:group') - # Compute the normalized target name for the option and give - # that to the node as an id - target_name = _make_anchor_target(currgroup, sig) - signode['ids'].append(target_name) - self.state.document.note_explicit_target(signode) - # Store the location of the option definition for later use in - # resolving cross-references - # FIXME: This should take the source namespace into account, too - cached_options[target_name] = self.env.docname - - -class ConfigDomain(Domain): - """oslo.config domain.""" - name = 'oslo.config' - label = 'oslo.config' - object_types = { - 'configoption': ObjType('configuration option', 'option'), - } - directives = { - 'group': ConfigGroup, - 'option': ConfigOption, - } - roles = { - 'option': ConfigOptXRefRole(), - 'group': ConfigGroupXRefRole(), - } - initial_data = { - 'options': {}, - 'groups': {}, - } - - def resolve_xref(self, env, fromdocname, builder, - typ, target, node, contnode): - if typ == 'option': - group_name, option_name = target.split('.', 1) - return make_refnode( - builder, - fromdocname, - env.domaindata['oslo.config']['options'][target], - target, - contnode, - option_name, - ) - if typ == 'group': - return make_refnode( - builder, - fromdocname, - env.domaindata['oslo.config']['groups'][target], - target, - contnode, - target, - ) - return None - - -def setup(app): - # NOTE(dhellmann): Try to turn off lazy translation from oslo_i18n - # so any translated help text or deprecation messages associated - # with configuration options are treated as regular strings - # instead of Message objects. Unfortunately this is a bit - # order-dependent, and so it's still possible that importing code - # from another module such as through the autodoc features, or - # even through the plugin scanner, will turn lazy evaluation back - # on. - oslo_i18n.enable_lazy(False) - app.add_directive('show-options', ShowOptionsDirective) - app.add_domain(ConfigDomain) diff --git a/oslo_config/tests/__init__.py b/oslo_config/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/oslo_config/tests/test_cfg.py b/oslo_config/tests/test_cfg.py deleted file mode 100644 index 8ae6e31..0000000 --- a/oslo_config/tests/test_cfg.py +++ /dev/null @@ -1,4764 +0,0 @@ -# Copyright 2014 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import argparse -import errno -import functools -import os -import shutil -import sys -import tempfile - -import fixtures -import mock -from oslotest import base -import six -from six import moves -import testscenarios - -from oslo_config import cfg -from oslo_config import types - -load_tests = testscenarios.load_tests_apply_scenarios - - -class ExceptionsTestCase(base.BaseTestCase): - - def test_error(self): - msg = str(cfg.Error('foobar')) - self.assertEqual('foobar', msg) - - def test_args_already_parsed_error(self): - msg = str(cfg.ArgsAlreadyParsedError('foobar')) - self.assertEqual('arguments already parsed: foobar', msg) - - def test_no_such_opt_error(self): - msg = str(cfg.NoSuchOptError('foo')) - self.assertEqual('no such option foo in group [DEFAULT]', msg) - - def test_no_such_opt_error_with_group(self): - msg = str(cfg.NoSuchOptError('foo', cfg.OptGroup('bar'))) - self.assertEqual('no such option foo in group [bar]', msg) - - def test_no_such_group_error(self): - msg = str(cfg.NoSuchGroupError('bar')) - self.assertEqual('no such group [bar]', msg) - - def test_duplicate_opt_error(self): - msg = str(cfg.DuplicateOptError('foo')) - self.assertEqual('duplicate option: foo', msg) - - def test_required_opt_error(self): - msg = str(cfg.RequiredOptError('foo')) - self.assertEqual('value required for option foo in group [DEFAULT]', - msg) - - def test_required_opt_error_with_group(self): - msg = str(cfg.RequiredOptError('foo', cfg.OptGroup('bar'))) - self.assertEqual('value required for option foo in group [bar]', msg) - - def test_template_substitution_error(self): - msg = str(cfg.TemplateSubstitutionError('foobar')) - self.assertEqual('template substitution error: foobar', msg) - - def test_config_files_not_found_error(self): - msg = str(cfg.ConfigFilesNotFoundError(['foo', 'bar'])) - self.assertEqual('Failed to find some config files: foo,bar', msg) - - def test_config_files_permission_denied_error(self): - msg = str(cfg.ConfigFilesPermissionDeniedError(['foo', 'bar'])) - self.assertEqual('Failed to open some config files: foo,bar', msg) - - def test_config_dir_not_found_error(self): - msg = str(cfg.ConfigDirNotFoundError('foobar')) - self.assertEqual('Failed to read config file directory: foobar', msg) - - def test_config_file_parse_error(self): - msg = str(cfg.ConfigFileParseError('foo', 'foobar')) - self.assertEqual('Failed to parse foo: foobar', msg) - - -class BaseTestCase(base.BaseTestCase): - - class TestConfigOpts(cfg.ConfigOpts): - def __call__(self, args=None, default_config_files=[]): - return cfg.ConfigOpts.__call__( - self, - args=args, - prog='test', - version='1.0', - usage='%(prog)s FOO BAR', - default_config_files=default_config_files, - validate_default_values=True) - - def setUp(self): - super(BaseTestCase, self).setUp() - self.useFixture(fixtures.NestedTempfile()) - self.conf = self.TestConfigOpts() - - self.tempdirs = [] - - def create_tempfiles(self, files, ext='.conf'): - tempfiles = [] - for (basename, contents) in files: - if not os.path.isabs(basename): - (fd, path) = tempfile.mkstemp(prefix=basename, suffix=ext) - else: - path = basename + ext - fd = os.open(path, os.O_CREAT | os.O_WRONLY) - tempfiles.append(path) - try: - os.write(fd, contents.encode('utf-8')) - finally: - os.close(fd) - return tempfiles - - -class UsageTestCase(BaseTestCase): - - def test_print_usage(self): - f = moves.StringIO() - self.conf([]) - self.conf.print_usage(file=f) - self.assertTrue('usage: test FOO BAR' in f.getvalue()) - self.assertTrue('optional:' not in f.getvalue()) - - -class HelpTestCase(BaseTestCase): - - def test_print_help(self): - f = moves.StringIO() - self.conf([]) - self.conf.print_help(file=f) - self.assertTrue('usage: test FOO BAR' in f.getvalue()) - self.assertTrue('optional' in f.getvalue()) - self.assertTrue('-h, --help' in f.getvalue()) - - def test_print_sorted_help(self): - f = moves.StringIO() - self.conf.register_cli_opt(cfg.StrOpt('abc')) - self.conf.register_cli_opt(cfg.StrOpt('zba')) - self.conf.register_cli_opt(cfg.StrOpt('ghi')) - self.conf.register_cli_opt(cfg.StrOpt('deb')) - self.conf([]) - self.conf.print_help(file=f) - zba = f.getvalue().find('--zba') - abc = f.getvalue().find('--abc') - ghi = f.getvalue().find('--ghi') - deb = f.getvalue().find('--deb') - list = [abc, deb, ghi, zba] - self.assertEqual(sorted(list), list) - - def test_print_sorted_help_with_positionals(self): - f = moves.StringIO() - self.conf.register_cli_opt(cfg.StrOpt('pst', positional=True)) - self.conf.register_cli_opt(cfg.StrOpt('abc')) - self.conf.register_cli_opt(cfg.StrOpt('zba')) - self.conf.register_cli_opt(cfg.StrOpt('ghi')) - self.conf([]) - self.conf.print_help(file=f) - zba = f.getvalue().find('--zba') - abc = f.getvalue().find('--abc') - ghi = f.getvalue().find('--ghi') - list = [abc, ghi, zba] - self.assertEqual(sorted(list), list) - - -class FindConfigFilesTestCase(BaseTestCase): - - def test_find_config_files(self): - config_files = [os.path.expanduser('~/.blaa/blaa.conf'), - '/etc/foo.conf'] - - self.useFixture(fixtures.MonkeyPatch('sys.argv', ['foo'])) - self.useFixture(fixtures.MonkeyPatch('os.path.exists', - lambda p: p in config_files)) - - self.assertEqual(cfg.find_config_files(project='blaa'), config_files) - - def test_find_config_files_with_extension(self): - config_files = ['/etc/foo.json'] - - self.useFixture(fixtures.MonkeyPatch('sys.argv', ['foo'])) - self.useFixture(fixtures.MonkeyPatch('os.path.exists', - lambda p: p in config_files)) - - self.assertEqual(cfg.find_config_files(project='blaa'), []) - self.assertEqual(cfg.find_config_files(project='blaa', - extension='.json'), - config_files) - - -class DefaultConfigFilesTestCase(BaseTestCase): - - def test_use_default(self): - self.conf.register_opt(cfg.StrOpt('foo')) - paths = self.create_tempfiles([('foo-', '[DEFAULT]\n''foo = bar\n')]) - - self.conf.register_cli_opt(cfg.StrOpt('config-file-foo')) - self.conf(args=['--config-file-foo', 'foo.conf'], - default_config_files=[paths[0]]) - - self.assertEqual([paths[0]], self.conf.config_file) - self.assertEqual('bar', self.conf.foo) - - def test_do_not_use_default_multi_arg(self): - self.conf.register_opt(cfg.StrOpt('foo')) - paths = self.create_tempfiles([('foo-', '[DEFAULT]\n''foo = bar\n')]) - - self.conf(args=['--config-file', paths[0]], - default_config_files=['bar.conf']) - - self.assertEqual([paths[0]], self.conf.config_file) - self.assertEqual('bar', self.conf.foo) - - def test_do_not_use_default_single_arg(self): - self.conf.register_opt(cfg.StrOpt('foo')) - paths = self.create_tempfiles([('foo-', '[DEFAULT]\n''foo = bar\n')]) - - self.conf(args=['--config-file=' + paths[0]], - default_config_files=['bar.conf']) - - self.assertEqual([paths[0]], self.conf.config_file) - self.assertEqual('bar', self.conf.foo) - - def test_no_default_config_file(self): - self.conf(args=[]) - self.assertEqual([], self.conf.config_file) - - def test_find_default_config_file(self): - paths = self.create_tempfiles([('def', '[DEFAULT]')]) - - self.useFixture(fixtures.MonkeyPatch( - 'oslo_config.cfg.find_config_files', - lambda project, prog: paths)) - - self.conf(args=[], default_config_files=None) - self.assertEqual(paths, self.conf.config_file) - - def test_default_config_file(self): - paths = self.create_tempfiles([('def', '[DEFAULT]')]) - - self.conf(args=[], default_config_files=paths) - - self.assertEqual(paths, self.conf.config_file) - - def test_default_config_file_with_value(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - - paths = self.create_tempfiles([('def', '[DEFAULT]\n''foo = bar\n')]) - - self.conf(args=[], default_config_files=paths) - - self.assertEqual(paths, self.conf.config_file) - self.assertEqual('bar', self.conf.foo) - - def test_default_config_file_priority(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - - paths = self.create_tempfiles([('def', '[DEFAULT]\n''foo = bar\n')]) - - self.conf(args=['--foo=blaa'], default_config_files=paths) - - self.assertEqual(paths, self.conf.config_file) - self.assertEqual('blaa', self.conf.foo) - - -class CliOptsTestCase(BaseTestCase): - """Test CLI Options. - - Each test scenario takes a name for the scenarios, as well as a dict: - opt_class - class of the type of option that should be tested - default - a default value for the option - cli_args - a list containing a representation of an input command line - value - the result value that is expected to be found - deps - a tuple of deprecated name/group - """ - - IPv4Opt = functools.partial(cfg.IPOpt, version=4) - IPv6Opt = functools.partial(cfg.IPOpt, version=6) - - multi_int = functools.partial(cfg.MultiOpt, item_type=types.Integer()) - multi_float = functools.partial(cfg.MultiOpt, item_type=types.Float()) - multi_string = functools.partial(cfg.MultiOpt, item_type=types.String()) - - scenarios = [ - ('str_default', - dict(opt_class=cfg.StrOpt, default=None, cli_args=[], value=None, - deps=(None, None))), - ('str_arg', - dict(opt_class=cfg.StrOpt, default=None, cli_args=['--foo', 'bar'], - value='bar', deps=(None, None))), - ('str_arg_deprecated_name', - dict(opt_class=cfg.StrOpt, default=None, - cli_args=['--oldfoo', 'bar'], value='bar', - deps=('oldfoo', None))), - ('str_arg_deprecated_group', - dict(opt_class=cfg.StrOpt, default=None, - cli_args=['--old-foo', 'bar'], value='bar', - deps=(None, 'old'))), - ('str_arg_deprecated_group_default', - dict(opt_class=cfg.StrOpt, default=None, cli_args=['--foo', 'bar'], - value='bar', deps=(None, 'DEFAULT'))), - ('str_arg_deprecated_group_and_name', - dict(opt_class=cfg.StrOpt, default=None, - cli_args=['--old-oof', 'bar'], value='bar', - deps=('oof', 'old'))), - ('bool_default', - dict(opt_class=cfg.BoolOpt, default=False, - cli_args=[], value=False, deps=(None, None))), - ('bool_arg', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--foo'], value=True, deps=(None, None))), - ('bool_arg_deprecated_name', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--oldfoo'], value=True, - deps=('oldfoo', None))), - ('bool_arg_deprecated_group', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--old-foo'], value=True, - deps=(None, 'old'))), - ('bool_arg_deprecated_group_default', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--foo'], value=True, - deps=(None, 'DEFAULT'))), - ('bool_arg_deprecated_group_and_name', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--old-oof'], value=True, - deps=('oof', 'old'))), - ('bool_arg_inverse', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--foo', '--nofoo'], value=False, deps=(None, None))), - ('bool_arg_inverse_deprecated_name', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--oldfoo', '--nooldfoo'], value=False, - deps=('oldfoo', None))), - ('bool_arg_inverse_deprecated_group', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--old-foo', '--old-nofoo'], value=False, - deps=(None, 'old'))), - ('bool_arg_inverse_deprecated_group_default', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--foo', '--nofoo'], value=False, - deps=(None, 'DEFAULT'))), - ('bool_arg_inverse_deprecated_group_and_name', - dict(opt_class=cfg.BoolOpt, default=None, - cli_args=['--old-oof', '--old-nooof'], value=False, - deps=('oof', 'old'))), - ('int_default', - dict(opt_class=cfg.IntOpt, default=10, - cli_args=[], value=10, deps=(None, None))), - ('int_arg', - dict(opt_class=cfg.IntOpt, default=None, - cli_args=['--foo=20'], value=20, deps=(None, None))), - ('int_arg_deprecated_name', - dict(opt_class=cfg.IntOpt, default=None, - cli_args=['--oldfoo=20'], value=20, deps=('oldfoo', None))), - ('int_arg_deprecated_group', - dict(opt_class=cfg.IntOpt, default=None, - cli_args=['--old-foo=20'], value=20, deps=(None, 'old'))), - ('int_arg_deprecated_group_default', - dict(opt_class=cfg.IntOpt, default=None, - cli_args=['--foo=20'], value=20, deps=(None, 'DEFAULT'))), - ('int_arg_deprecated_group_and_name', - dict(opt_class=cfg.IntOpt, default=None, - cli_args=['--old-oof=20'], value=20, deps=('oof', 'old'))), - ('float_default', - dict(opt_class=cfg.FloatOpt, default=1.0, - cli_args=[], value=1.0, deps=(None, None))), - ('float_arg', - dict(opt_class=cfg.FloatOpt, default=None, - cli_args=['--foo', '2.0'], value=2.0, deps=(None, None))), - ('float_arg_deprecated_name', - dict(opt_class=cfg.FloatOpt, default=None, - cli_args=['--oldfoo', '2.0'], value=2.0, deps=('oldfoo', None))), - ('float_arg_deprecated_group', - dict(opt_class=cfg.FloatOpt, default=None, - cli_args=['--old-foo', '2.0'], value=2.0, deps=(None, 'old'))), - ('float_arg_deprecated_group_default', - dict(opt_class=cfg.FloatOpt, default=None, - cli_args=['--foo', '2.0'], value=2.0, deps=(None, 'DEFAULT'))), - ('float_arg_deprecated_group_and_name', - dict(opt_class=cfg.FloatOpt, default=None, - cli_args=['--old-oof', '2.0'], value=2.0, deps=('oof', 'old'))), - ('float_default_as_integer', - dict(opt_class=cfg.FloatOpt, default=2, - cli_args=['--old-oof', '2.0'], value=2.0, deps=('oof', 'old'))), - ('ipv4addr_arg', - dict(opt_class=IPv4Opt, default=None, - cli_args=['--foo', '192.168.0.1'], value='192.168.0.1', - deps=(None, None))), - ('ipaddr_arg_implicitv4', - dict(opt_class=cfg.IPOpt, default=None, - cli_args=['--foo', '192.168.0.1'], value='192.168.0.1', - deps=(None, None))), - ('ipaddr_arg_implicitv6', - dict(opt_class=cfg.IPOpt, default=None, - cli_args=['--foo', 'abcd:ef::1'], value='abcd:ef::1', - deps=(None, None))), - ('ipv6addr_arg', - dict(opt_class=IPv6Opt, default=None, - cli_args=['--foo', 'abcd:ef::1'], value='abcd:ef::1', - deps=(None, None))), - ('list_default', - dict(opt_class=cfg.ListOpt, default=['bar'], - cli_args=[], value=['bar'], deps=(None, None))), - ('list_arg', - dict(opt_class=cfg.ListOpt, default=None, - cli_args=['--foo', 'blaa,bar'], value=['blaa', 'bar'], - deps=(None, None))), - ('list_arg_with_spaces', - dict(opt_class=cfg.ListOpt, default=None, - cli_args=['--foo', 'blaa ,bar'], value=['blaa', 'bar'], - deps=(None, None))), - ('list_arg_deprecated_name', - dict(opt_class=cfg.ListOpt, default=None, - cli_args=['--oldfoo', 'blaa,bar'], value=['blaa', 'bar'], - deps=('oldfoo', None))), - ('list_arg_deprecated_group', - dict(opt_class=cfg.ListOpt, default=None, - cli_args=['--old-foo', 'blaa,bar'], value=['blaa', 'bar'], - deps=(None, 'old'))), - ('list_arg_deprecated_group_default', - dict(opt_class=cfg.ListOpt, default=None, - cli_args=['--foo', 'blaa,bar'], value=['blaa', 'bar'], - deps=(None, 'DEFAULT'))), - ('list_arg_deprecated_group_and_name', - dict(opt_class=cfg.ListOpt, default=None, - cli_args=['--old-oof', 'blaa,bar'], value=['blaa', 'bar'], - deps=('oof', 'old'))), - ('dict_default', - dict(opt_class=cfg.DictOpt, default={'foo': 'bar'}, - cli_args=[], value={'foo': 'bar'}, deps=(None, None))), - ('dict_arg', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--foo', 'key1:blaa,key2:bar'], - value={'key1': 'blaa', 'key2': 'bar'}, deps=(None, None))), - ('dict_arg_multiple_keys_last_wins', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--foo', 'key1:blaa', '--foo', 'key2:bar'], - value={'key2': 'bar'}, deps=(None, None))), - ('dict_arg_with_spaces', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--foo', 'key1:blaa ,key2:bar'], - value={'key1': 'blaa', 'key2': 'bar'}, deps=(None, None))), - ('dict_arg_deprecated_name', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--oldfoo', 'key1:blaa', '--oldfoo', 'key2:bar'], - value={'key2': 'bar'}, deps=('oldfoo', None))), - ('dict_arg_deprecated_group', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--old-foo', 'key1:blaa,key2:bar'], - value={'key1': 'blaa', 'key2': 'bar'}, deps=(None, 'old'))), - ('dict_arg_deprecated_group2', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--old-foo', 'key1:blaa', '--old-foo', 'key2:bar'], - value={'key2': 'bar'}, deps=(None, 'old'))), - ('dict_arg_deprecated_group_default', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--foo', 'key1:blaa', '--foo', 'key2:bar'], - value={'key2': 'bar'}, deps=(None, 'DEFAULT'))), - ('dict_arg_deprecated_group_and_name', - dict(opt_class=cfg.DictOpt, default=None, - cli_args=['--old-oof', 'key1:blaa,key2:bar'], - value={'key1': 'blaa', 'key2': 'bar'}, deps=('oof', 'old'))), - ('dict_arg_deprecated_group_and_name2', - 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'))), - ('uri_default', - dict(opt_class=cfg.URIOpt, default='http://example.com', - cli_args=[], value='http://example.com', deps=(None, None))), - ('uri_arg', - dict(opt_class=cfg.URIOpt, default=None, - cli_args=['--foo', 'http://example.com'], - value='http://example.com', deps=(None, None))), - ('multistr_default', - dict(opt_class=cfg.MultiStrOpt, default=['bar'], cli_args=[], - value=['bar'], deps=(None, None))), - ('multistr_arg', - dict(opt_class=cfg.MultiStrOpt, default=None, - cli_args=['--foo', 'blaa', '--foo', 'bar'], - value=['blaa', 'bar'], deps=(None, None))), - ('multistr_arg_deprecated_name', - dict(opt_class=cfg.MultiStrOpt, default=None, - cli_args=['--oldfoo', 'blaa', '--oldfoo', 'bar'], - value=['blaa', 'bar'], deps=('oldfoo', None))), - ('multistr_arg_deprecated_group', - dict(opt_class=cfg.MultiStrOpt, default=None, - cli_args=['--old-foo', 'blaa', '--old-foo', 'bar'], - value=['blaa', 'bar'], deps=(None, 'old'))), - ('multistr_arg_deprecated_group_default', - dict(opt_class=cfg.MultiStrOpt, default=None, - cli_args=['--foo', 'blaa', '--foo', 'bar'], - value=['blaa', 'bar'], deps=(None, 'DEFAULT'))), - ('multistr_arg_deprecated_group_and_name', - dict(opt_class=cfg.MultiStrOpt, default=None, - cli_args=['--old-oof', 'blaa', '--old-oof', 'bar'], - value=['blaa', 'bar'], deps=('oof', 'old'))), - ('multiopt_arg_int', - dict(opt_class=multi_int, default=None, - cli_args=['--foo', '1', '--foo', '2'], - value=[1, 2], deps=(None, None))), - ('multiopt_float_int', - dict(opt_class=multi_float, default=None, - cli_args=['--foo', '1.2', '--foo', '2.3'], - value=[1.2, 2.3], deps=(None, None))), - ('multiopt_string', - dict(opt_class=multi_string, default=None, - cli_args=['--foo', 'bar', '--foo', 'baz'], - value=["bar", "baz"], deps=(None, None))), - ] - - def test_cli(self): - - self.conf.register_cli_opt( - self.opt_class('foo', default=self.default, - deprecated_name=self.deps[0], - deprecated_group=self.deps[1])) - - self.conf(self.cli_args) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(self.value, self.conf.foo) - - -class CliSpecialOptsTestCase(BaseTestCase): - - def test_help(self): - self.useFixture(fixtures.MonkeyPatch('sys.stdout', moves.StringIO())) - self.assertRaises(SystemExit, self.conf, ['--help']) - self.assertTrue('FOO BAR' in sys.stdout.getvalue()) - self.assertTrue('--version' in sys.stdout.getvalue()) - self.assertTrue('--help' in sys.stdout.getvalue()) - self.assertTrue('--config-file' in sys.stdout.getvalue()) - - def test_version(self): - # In Python 3.4+, argparse prints the version on stdout; before 3.4, it - # printed it on stderr. - if sys.version_info >= (3, 4): - stream_name = 'stdout' - else: - stream_name = 'stderr' - self.useFixture(fixtures.MonkeyPatch("sys.%s" % stream_name, - moves.StringIO())) - self.assertRaises(SystemExit, self.conf, ['--version']) - self.assertTrue('1.0' in getattr(sys, stream_name).getvalue()) - - def test_config_file(self): - paths = self.create_tempfiles([('1', '[DEFAULT]'), - ('2', '[DEFAULT]')]) - - self.conf(['--config-file', paths[0], '--config-file', paths[1]]) - - self.assertEqual(paths, self.conf.config_file) - - -class PositionalTestCase(BaseTestCase): - - def _do_pos_test(self, opt_class, default, cli_args, value): - self.conf.register_cli_opt(opt_class('foo', - default=default, - positional=True)) - - self.conf(cli_args) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(value, self.conf.foo) - - def test_positional_str_none_default(self): - self._do_pos_test(cfg.StrOpt, None, [], None) - - def test_positional_str_default(self): - self._do_pos_test(cfg.StrOpt, 'bar', [], 'bar') - - def test_positional_str_arg(self): - self._do_pos_test(cfg.StrOpt, None, ['bar'], 'bar') - - def test_positional_int_none_default(self): - self._do_pos_test(cfg.IntOpt, None, [], None) - - def test_positional_int_default(self): - self._do_pos_test(cfg.IntOpt, 10, [], 10) - - def test_positional_int_arg(self): - self._do_pos_test(cfg.IntOpt, None, ['20'], 20) - - def test_positional_float_none_default(self): - self._do_pos_test(cfg.FloatOpt, None, [], None) - - def test_positional_float_default(self): - self._do_pos_test(cfg.FloatOpt, 1.0, [], 1.0) - - def test_positional_float_arg(self): - self._do_pos_test(cfg.FloatOpt, None, ['2.0'], 2.0) - - def test_positional_list_none_default(self): - self._do_pos_test(cfg.ListOpt, None, [], None) - - def test_positional_list_empty_default(self): - self._do_pos_test(cfg.ListOpt, [], [], []) - - def test_positional_list_default(self): - self._do_pos_test(cfg.ListOpt, ['bar'], [], ['bar']) - - def test_positional_list_arg(self): - self._do_pos_test(cfg.ListOpt, None, - ['blaa,bar'], ['blaa', 'bar']) - - def test_positional_dict_none_default(self): - self._do_pos_test(cfg.DictOpt, None, [], None) - - def test_positional_dict_empty_default(self): - self._do_pos_test(cfg.DictOpt, {}, [], {}) - - def test_positional_dict_default(self): - self._do_pos_test(cfg.DictOpt, {'key1': 'bar'}, [], {'key1': 'bar'}) - - def test_positional_dict_arg(self): - self._do_pos_test(cfg.DictOpt, None, - ['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_uri_default(self): - self._do_pos_test(cfg.URIOpt, 'http://example.com', [], - 'http://example.com') - - def test_positional_uri_none_default(self): - self._do_pos_test(cfg.URIOpt, None, [], None) - - def test_positional_uri_arg(self): - self._do_pos_test(cfg.URIOpt, None, ['http://example.com'], - 'http://example.com') - - def test_positional_multistr_none_default(self): - self._do_pos_test(cfg.MultiStrOpt, None, [], None) - - def test_positional_multistr_empty_default(self): - self._do_pos_test(cfg.MultiStrOpt, [], [], []) - - def test_positional_multistr_default(self): - self._do_pos_test(cfg.MultiStrOpt, ['bar'], [], ['bar']) - - def test_positional_multistr_arg(self): - self._do_pos_test(cfg.MultiStrOpt, None, - ['blaa', 'bar'], ['blaa', 'bar']) - - def test_positional_bool(self): - self.assertRaises(ValueError, cfg.BoolOpt, 'foo', positional=True) - - def test_required_positional_opt(self): - self.conf.register_cli_opt( - cfg.StrOpt('foo', required=True, positional=True)) - - self.conf(['bar']) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - def test_missing_required_cli_opt(self): - self.conf.register_cli_opt( - cfg.StrOpt('foo', required=True, positional=True)) - self.assertRaises(cfg.RequiredOptError, self.conf, []) - - def test_positional_opts_order(self): - self.conf.register_cli_opts(( - cfg.StrOpt('command', positional=True), - cfg.StrOpt('arg1', positional=True), - cfg.StrOpt('arg2', positional=True)) - ) - - self.conf(['command', 'arg1', 'arg2']) - - self.assertEqual('command', self.conf.command) - self.assertEqual('arg1', self.conf.arg1) - self.assertEqual('arg2', self.conf.arg2) - - def test_positional_opt_order(self): - self.conf.register_cli_opt( - cfg.StrOpt('command', positional=True)) - self.conf.register_cli_opt( - cfg.StrOpt('arg1', positional=True)) - self.conf.register_cli_opt( - cfg.StrOpt('arg2', positional=True)) - - self.conf(['command', 'arg1', 'arg2']) - - self.assertEqual('command', self.conf.command) - self.assertEqual('arg1', self.conf.arg1) - self.assertEqual('arg2', self.conf.arg2) - - def test_positional_opt_unregister(self): - command = cfg.StrOpt('command', positional=True) - arg1 = cfg.StrOpt('arg1', positional=True) - arg2 = cfg.StrOpt('arg2', positional=True) - self.conf.register_cli_opt(command) - self.conf.register_cli_opt(arg1) - self.conf.register_cli_opt(arg2) - - self.conf(['command', 'arg1', 'arg2']) - - self.assertEqual('command', self.conf.command) - self.assertEqual('arg1', self.conf.arg1) - self.assertEqual('arg2', self.conf.arg2) - - self.conf.reset() - - self.conf.unregister_opt(arg1) - self.conf.unregister_opt(arg2) - - arg0 = cfg.StrOpt('arg0', positional=True) - self.conf.register_cli_opt(arg0) - self.conf.register_cli_opt(arg1) - - self.conf(['command', 'arg0', 'arg1']) - - self.assertEqual('command', self.conf.command) - self.assertEqual('arg0', self.conf.arg0) - self.assertEqual('arg1', self.conf.arg1) - - -class ConfigFileOptsTestCase(BaseTestCase): - - def _do_deprecated_test(self, opt_class, value, result, key, - section='DEFAULT', - dname=None, dgroup=None): - self.conf.register_opt(opt_class('newfoo', - deprecated_name=dname, - deprecated_group=dgroup)) - - paths = self.create_tempfiles([('test', - '[' + section + ']\n' + - key + ' = ' + value + '\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'newfoo')) - self.assertEqual(result, self.conf.newfoo) - - def _do_dname_test_use(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'oldfoo', - dname='oldfoo') - - def _do_dgroup_test_use(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'newfoo', - section='old', dgroup='old') - - def _do_default_dgroup_test_use(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'newfoo', - section='DEFAULT', dgroup='DEFAULT') - - def _do_dgroup_and_dname_test_use(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'oof', - section='old', dgroup='old', dname='oof') - - def _do_dname_test_ignore(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'newfoo', - dname='oldfoo') - - def _do_dgroup_test_ignore(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'newfoo', - section='DEFAULT', dgroup='old') - - def _do_dgroup_and_dname_test_ignore(self, opt_class, value, result): - self._do_deprecated_test(opt_class, value, result, 'oof', - section='old', dgroup='old', dname='oof') - - def test_conf_file_str_default(self): - self.conf.register_opt(cfg.StrOpt('foo', default='bar')) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - def test_conf_file_str_value(self): - self.conf.register_opt(cfg.StrOpt('foo')) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - def test_conf_file_str_value_override(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('baaar', self.conf.foo) - - def test_conf_file_str_value_override_use_deprecated(self): - """last option should always win, even if last uses deprecated.""" - self.conf.register_cli_opt( - cfg.StrOpt('newfoo', deprecated_name='oldfoo')) - - paths = self.create_tempfiles([('0', - '[DEFAULT]\n' - 'newfoo = middle\n'), - ('1', - '[DEFAULT]\n' - 'oldfoo = last\n')]) - - self.conf(['--newfoo', 'first', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'newfoo')) - self.assertFalse(hasattr(self.conf, 'oldfoo')) - self.assertEqual('last', self.conf.newfoo) - - def test_conf_file_str_use_dname(self): - self._do_dname_test_use(cfg.StrOpt, 'value1', 'value1') - - def test_conf_file_str_use_dgroup(self): - self._do_dgroup_test_use(cfg.StrOpt, 'value1', 'value1') - - def test_conf_file_str_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.StrOpt, 'value1', 'value1') - - def test_conf_file_str_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.StrOpt, 'value1', 'value1') - - def test_conf_file_str_ignore_dname(self): - self._do_dname_test_ignore(cfg.StrOpt, 'value2', 'value2') - - def test_conf_file_str_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.StrOpt, 'value2', 'value2') - - 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('bar', self.conf.foo) - - def test_conf_file_bool_default_none(self): - self.conf.register_opt(cfg.BoolOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertIsNone(self.conf.foo) - - def test_conf_file_bool_default_false(self): - self.conf.register_opt(cfg.BoolOpt('foo', default=False)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertFalse(self.conf.foo) - - def test_conf_file_bool_value(self): - self.conf.register_opt(cfg.BoolOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = true\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertTrue(self.conf.foo) - - def test_conf_file_bool_cli_value_override(self): - self.conf.register_cli_opt(cfg.BoolOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = 0\n')]) - - self.conf(['--foo', - '--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertFalse(self.conf.foo) - - def test_conf_file_bool_cli_inverse_override(self): - self.conf.register_cli_opt(cfg.BoolOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = true\n')]) - - self.conf(['--nofoo', - '--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertTrue(self.conf.foo) - - def test_conf_file_bool_cli_order_override(self): - self.conf.register_cli_opt(cfg.BoolOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = false\n')]) - - self.conf(['--config-file', paths[0], - '--foo']) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertTrue(self.conf.foo) - - def test_conf_file_bool_file_value_override(self): - self.conf.register_cli_opt(cfg.BoolOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = 0\n'), - ('2', - '[DEFAULT]\n' - 'foo = yes\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertTrue(self.conf.foo) - - def test_conf_file_bool_use_dname(self): - self._do_dname_test_use(cfg.BoolOpt, 'yes', True) - - def test_conf_file_bool_use_dgroup(self): - self._do_dgroup_test_use(cfg.BoolOpt, 'yes', True) - - def test_conf_file_bool_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.BoolOpt, 'yes', True) - - def test_conf_file_bool_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.BoolOpt, 'yes', True) - - def test_conf_file_bool_ignore_dname(self): - self._do_dname_test_ignore(cfg.BoolOpt, 'no', False) - - def test_conf_file_bool_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.BoolOpt, 'no', False) - - def test_conf_file_bool_ignore_group_and_dname(self): - self._do_dgroup_and_dname_test_ignore(cfg.BoolOpt, 'no', False) - - def test_conf_file_int_default(self): - self.conf.register_opt(cfg.IntOpt('foo', default=666)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(666, self.conf.foo) - - def test_conf_file_int_string_default_type(self): - self.conf.register_opt(cfg.IntOpt('foo', default='666')) - self.conf([]) - self.assertEqual(666, self.conf.foo) - - def test_conf_file_int_value(self): - self.conf.register_opt(cfg.IntOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(666, self.conf.foo) - - def test_conf_file_int_value_override(self): - self.conf.register_cli_opt(cfg.IntOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = 66\n'), - ('2', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(666, self.conf.foo) - - def test_conf_file_int_min_max(self): - self.conf.register_opt(cfg.IntOpt('foo', min=1, max=5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 10\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_int_min_greater_max(self): - self.assertRaises(ValueError, cfg.IntOpt, 'foo', min=5, max=1) - - def test_conf_file_int_use_dname(self): - self._do_dname_test_use(cfg.IntOpt, '66', 66) - - def test_conf_file_int_use_dgroup(self): - self._do_dgroup_test_use(cfg.IntOpt, '66', 66) - - def test_conf_file_int_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.IntOpt, '66', 66) - - def test_conf_file_int_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.IntOpt, '66', 66) - - def test_conf_file_int_ignore_dname(self): - self._do_dname_test_ignore(cfg.IntOpt, '64', 64) - - def test_conf_file_int_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.IntOpt, '64', 64) - - def test_conf_file_int_ignore_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_ignore(cfg.IntOpt, '64', 64) - - def test_conf_file_float_default(self): - self.conf.register_opt(cfg.FloatOpt('foo', default=6.66)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(6.66, self.conf.foo) - - 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')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(6.66, self.conf.foo) - - def test_conf_file_float_value_override(self): - self.conf.register_cli_opt(cfg.FloatOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = 6.6\n'), - ('2', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(6.66, self.conf.foo) - - def test_conf_file_float_use_dname(self): - self._do_dname_test_use(cfg.FloatOpt, '66.54', 66.54) - - def test_conf_file_float_use_dgroup(self): - self._do_dgroup_test_use(cfg.FloatOpt, '66.54', 66.54) - - def test_conf_file_float_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.FloatOpt, '66.54', 66.54) - - def test_conf_file_float_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.FloatOpt, '66.54', 66.54) - - def test_conf_file_float_ignore_dname(self): - self._do_dname_test_ignore(cfg.FloatOpt, '64.54', 64.54) - - def test_conf_file_float_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.FloatOpt, '64.54', 64.54) - - def test_conf_file_float_ignore_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_ignore(cfg.FloatOpt, '64.54', 64.54) - - def test_conf_file_float_min_max_above_max(self): - self.conf.register_opt(cfg.FloatOpt('foo', min=1.1, max=5.5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 10.5\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_float_only_max_above_max(self): - self.conf.register_opt(cfg.FloatOpt('foo', max=5.5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 10.5\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_float_min_max_below_min(self): - self.conf.register_opt(cfg.FloatOpt('foo', min=1.1, max=5.5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 0.5\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_float_only_min_below_min(self): - self.conf.register_opt(cfg.FloatOpt('foo', min=1.1)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 0.5\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_float_min_max_in_range(self): - self.conf.register_opt(cfg.FloatOpt('foo', min=1.1, max=5.5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 4.5\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(4.5, self.conf.foo) - - def test_conf_file_float_only_max_in_range(self): - self.conf.register_opt(cfg.FloatOpt('foo', max=5.5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 4.5\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(4.5, self.conf.foo) - - def test_conf_file_float_only_min_in_range(self): - self.conf.register_opt(cfg.FloatOpt('foo', min=3.5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 4.5\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(4.5, self.conf.foo) - - def test_conf_file_float_min_greater_max(self): - self.assertRaises(ValueError, cfg.FloatOpt, 'foo', min=5.5, max=1.5) - - def test_conf_file_list_default(self): - self.conf.register_opt(cfg.ListOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(['bar'], self.conf.foo) - - 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')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(['bar'], self.conf.foo) - - def test_conf_file_list_value_override(self): - self.conf.register_cli_opt(cfg.ListOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = bar,bar\n'), - ('2', - '[DEFAULT]\n' - 'foo = b,a,r\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(['b', 'a', 'r'], self.conf.foo) - - 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([1, 2], self.conf.foo) - - def test_conf_file_list_use_dname(self): - self._do_dname_test_use(cfg.ListOpt, 'a,b,c', ['a', 'b', 'c']) - - def test_conf_file_list_use_dgroup(self): - self._do_dgroup_test_use(cfg.ListOpt, 'a,b,c', ['a', 'b', 'c']) - - def test_conf_file_list_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.ListOpt, 'a,b,c', ['a', 'b', 'c']) - - def test_conf_file_list_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.ListOpt, 'a,b,c', - ['a', 'b', 'c']) - - def test_conf_file_list_ignore_dname(self): - self._do_dname_test_ignore(cfg.ListOpt, 'd,e,f', ['d', 'e', 'f']) - - def test_conf_file_list_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.ListOpt, 'd,e,f', ['d', 'e', 'f']) - - def test_conf_file_list_ignore_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_ignore( - cfg.ListOpt, 'd,e,f', ['d', 'e', 'f']) - - def test_conf_file_list_spaces_use_dname(self): - self._do_dname_test_use(cfg.ListOpt, 'a, b, c', ['a', 'b', 'c']) - - def test_conf_file_list_spaces_use_dgroup(self): - self._do_dgroup_test_use(cfg.ListOpt, 'a, b, c', ['a', 'b', 'c']) - - def test_conf_file_list_spaces_use_default_dgroup(self): - self._do_default_dgroup_test_use( - cfg.ListOpt, 'a, b, c', ['a', 'b', 'c']) - - def test_conf_file_list_spaces_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use( - cfg.ListOpt, 'a, b, c', ['a', 'b', 'c']) - - def test_conf_file_list_spaces_ignore_dname(self): - self._do_dname_test_ignore(cfg.ListOpt, 'd, e, f', ['d', 'e', 'f']) - - def test_conf_file_list_spaces_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.ListOpt, 'd, e, f', ['d', 'e', 'f']) - - def test_conf_file_list_spaces_ignore_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_ignore(cfg.ListOpt, 'd, e, f', - ['d', 'e', 'f']) - - def test_conf_file_dict_default(self): - self.conf.register_opt(cfg.DictOpt('foo', default={'key': 'bar'})) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual({'key': 'bar'}, self.conf.foo) - - def test_conf_file_dict_value(self): - self.conf.register_opt(cfg.DictOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = key:bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual({'key': 'bar'}, self.conf.foo) - - def test_conf_file_dict_colon_in_value(self): - self.conf.register_opt(cfg.DictOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = key:bar:baz\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual({'key': 'bar:baz'}, self.conf.foo) - - def test_conf_file_dict_value_no_colon(self): - self.conf.register_opt(cfg.DictOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = key:bar,baz\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - self.assertRaises(ValueError, getattr, self.conf, 'foo') - - def test_conf_file_dict_value_duplicate_key(self): - self.conf.register_opt(cfg.DictOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = key:bar,key:baz\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, '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', - deprecated_name='oldfoo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = key1:bar1\n'), - ('2', - '[DEFAULT]\n' - 'oldfoo = key2:bar2\n' - 'oldfoo = key3:bar3\n')]) - - self.conf(['--foo', 'key0:bar0', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - - self.assertEqual({'key3': 'bar3'}, self.conf.foo) - - def test_conf_file_dict_deprecated(self): - self.conf.register_opt(cfg.DictOpt('newfoo', deprecated_name='oldfoo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'oldfoo= key1:bar1\n' - 'oldfoo = key2:bar2\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'newfoo')) - self.assertEqual({'key2': 'bar2'}, self.conf.newfoo) - - def test_conf_file_dict_value_override(self): - self.conf.register_cli_opt(cfg.DictOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = key:bar,key2:bar\n'), - ('2', - '[DEFAULT]\n' - 'foo = k1:v1,k2:v2\n')]) - - self.conf(['--foo', 'x:y,x2:y2', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual({'k1': 'v1', 'k2': 'v2'}, self.conf.foo) - - def test_conf_file_dict_use_dname(self): - self._do_dname_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_use_dgroup(self): - self._do_dgroup_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_ignore_dname(self): - self._do_dname_test_ignore(cfg.DictOpt, - 'k1:d,k2:e,k3:f', - {'k1': 'd', 'k2': 'e', 'k3': 'f'}) - - def test_conf_file_dict_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.DictOpt, - 'k1:d,k2:e,k3:f', - {'k1': 'd', 'k2': 'e', 'k3': 'f'}) - - def test_conf_file_dict_ignore_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_ignore(cfg.DictOpt, - 'k1:d,k2:e,k3:f', - {'k1': 'd', - 'k2': 'e', - 'k3': 'f'}) - - def test_conf_file_dict_spaces_use_dname(self): - self._do_dname_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_spaces_use_dgroup(self): - self._do_dgroup_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_spaces_use_default_dgroup(self): - self._do_default_dgroup_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_spaces_use_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_use(cfg.DictOpt, - 'k1:a,k2:b,k3:c', - {'k1': 'a', 'k2': 'b', 'k3': 'c'}) - - def test_conf_file_dict_spaces_ignore_dname(self): - self._do_dname_test_ignore(cfg.DictOpt, - 'k1:d,k2:e,k3:f', - {'k1': 'd', 'k2': 'e', 'k3': 'f'}) - - def test_conf_file_dict_spaces_ignore_dgroup(self): - self._do_dgroup_test_ignore(cfg.DictOpt, - 'k1:d,k2:e,k3:f', - {'k1': 'd', 'k2': 'e', 'k3': 'f'}) - - def test_conf_file_dict_spaces_ignore_dgroup_and_dname(self): - self._do_dgroup_and_dname_test_ignore(cfg.DictOpt, - 'k1:d,k2:e,k3:f', - {'k1': 'd', - '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_port_list(self): - self.conf.register_opt(cfg.ListOpt('foo', item_type=types.Port())) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 22, 80\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual([22, 80], self.conf.foo) - - def test_conf_file_port_list_default(self): - self.conf.register_opt(cfg.ListOpt('foo', item_type=types.Port(), - default=[55, 77])) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 22, 80\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual([22, 80], self.conf.foo) - - def test_conf_file_port_list_only_default(self): - self.conf.register_opt(cfg.ListOpt('foo', item_type=types.Port(), - default=[55, 77])) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual([55, 77], self.conf.foo) - - def test_conf_file_port_list_outside_range(self): - self.conf.register_opt(cfg.ListOpt('foo', item_type=types.Port())) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 1,65536\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_port_min_max_above_max(self): - self.conf.register_opt(cfg.PortOpt('foo', min=1, max=5)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 10\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_port_only_max_above_max(self): - self.conf.register_opt(cfg.PortOpt('foo', max=500)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 600\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_port_min_max_below_min(self): - self.conf.register_opt(cfg.PortOpt('foo', min=100, max=500)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 99\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_port_only_min_below_min(self): - self.conf.register_opt(cfg.PortOpt('foo', min=1025)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 1024\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_port_min_max_in_range(self): - self.conf.register_opt(cfg.PortOpt('foo', min=1025, max=6000)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 2500\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(2500, self.conf.foo) - - def test_conf_file_port_only_max_in_range(self): - self.conf.register_opt(cfg.PortOpt('foo', max=5000)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 45\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(45, self.conf.foo) - - def test_conf_file_port_only_min_in_range(self): - self.conf.register_opt(cfg.PortOpt('foo', min=35)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 45\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(45, self.conf.foo) - - def test_conf_file_port_min_greater_max(self): - self.assertRaises(ValueError, cfg.PortOpt, 'foo', min=55, max=15) - - def test_conf_file_multistr_default(self): - self.conf.register_opt(cfg.MultiStrOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(['bar'], self.conf.foo) - - def test_conf_file_multistr_value(self): - self.conf.register_opt(cfg.MultiStrOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual(['bar'], self.conf.foo) - - def test_conf_file_multistr_values_append_deprecated(self): - self.conf.register_cli_opt(cfg.MultiStrOpt('foo', - deprecated_name='oldfoo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = bar1\n'), - ('2', - '[DEFAULT]\n' - 'oldfoo = bar2\n' - 'oldfoo = bar3\n')]) - - self.conf(['--foo', 'bar0', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - - self.assertEqual(['bar0', 'bar1', 'bar2', 'bar3'], self.conf.foo) - - def test_conf_file_multistr_values_append(self): - self.conf.register_cli_opt(cfg.MultiStrOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = bar1\n'), - ('2', - '[DEFAULT]\n' - 'foo = bar2\n' - 'foo = bar3\n')]) - - self.conf(['--foo', 'bar0', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - - self.assertEqual(['bar0', 'bar1', 'bar2', 'bar3'], self.conf.foo) - - def test_conf_file_multistr_deprecated(self): - self.conf.register_opt( - cfg.MultiStrOpt('newfoo', deprecated_name='oldfoo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'oldfoo= bar1\n' - 'oldfoo = bar2\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'newfoo')) - self.assertEqual(['bar1', 'bar2'], self.conf.newfoo) - - def test_conf_file_multiple_opts(self): - self.conf.register_opts([cfg.StrOpt('foo'), cfg.StrOpt('bar')]) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n' - 'bar = foo\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual('foo', self.conf.bar) - - def test_conf_file_raw_value(self): - self.conf.register_opt(cfg.StrOpt('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar-%08x\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar-%08x', self.conf.foo) - - def test_conf_file_sorted_group(self): - # Create enough groups for the sorted problem to appear - for i in range(10): - group = cfg.OptGroup('group%s' % i, 'options') - self.conf.register_group(group) - self.conf.register_cli_opt(cfg.StrOpt('opt1'), group=group) - - paths = self.create_tempfiles( - [('test', '[group1]\nopt1 = foo\n[group2]\nopt2 = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertEqual('foo', self.conf.group1.opt1) - - -class ConfigFileReloadTestCase(BaseTestCase): - - def test_conf_files_reload(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('baar', self.conf.foo) - - shutil.copy(paths[1], paths[0]) - - self.conf.reload_config_files() - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('baaar', self.conf.foo) - - def test_conf_files_reload_default(self): - self.conf.register_cli_opt(cfg.StrOpt('foo1')) - self.conf.register_cli_opt(cfg.StrOpt('foo2')) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo1 = default1\n'), - ('2', - '[DEFAULT]\n' - 'foo2 = default2\n')]) - - paths_change = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo1 = change_default1\n'), - ('2', - '[DEFAULT]\n' - 'foo2 = change_default2\n')]) - - self.conf(args=[], default_config_files=paths) - self.assertTrue(hasattr(self.conf, 'foo1')) - self.assertEqual('default1', self.conf.foo1) - self.assertTrue(hasattr(self.conf, 'foo2')) - self.assertEqual('default2', self.conf.foo2) - - shutil.copy(paths_change[0], paths[0]) - shutil.copy(paths_change[1], paths[1]) - - self.conf.reload_config_files() - self.assertTrue(hasattr(self.conf, 'foo1')) - self.assertEqual('change_default1', self.conf.foo1) - self.assertTrue(hasattr(self.conf, 'foo2')) - self.assertEqual('change_default2', self.conf.foo2) - - def test_conf_files_reload_file_not_found(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', required=True)) - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = baar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('baar', self.conf.foo) - - os.remove(paths[0]) - - self.conf.reload_config_files() - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('baar', self.conf.foo) - - def test_conf_files_reload_error(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', required=True)) - self.conf.register_cli_opt(cfg.StrOpt('foo1', required=True)) - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = test1\n' - 'foo1 = test11\n'), - ('2', - '[DEFAULT]\n' - 'foo2 = test2\n' - 'foo3 = test22\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('test1', self.conf.foo) - self.assertTrue(hasattr(self.conf, 'foo1')) - self.assertEqual('test11', self.conf.foo1) - - shutil.copy(paths[1], paths[0]) - - self.conf.reload_config_files() - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('test1', self.conf.foo) - self.assertTrue(hasattr(self.conf, 'foo1')) - self.assertEqual('test11', self.conf.foo1) - - -class ConfigFileMutateTestCase(BaseTestCase): - def setUp(self): - super(ConfigFileMutateTestCase, self).setUp() - self.my_group = cfg.OptGroup('group', 'group options') - self.conf.register_group(self.my_group) - - def _test_conf_files_mutate(self): - paths = self.create_tempfiles([ - ('1', '[DEFAULT]\n' - 'foo = old_foo\n' - '[group]\n' - 'boo = old_boo\n'), - ('2', '[DEFAULT]\n' - 'foo = new_foo\n' - '[group]\n' - 'boo = new_boo\n')]) - - self.conf(['--config-file', paths[0]]) - shutil.copy(paths[1], paths[0]) - return self.conf.mutate_config_files() - - def test_conf_files_mutate_none(self): - """Test that immutable opts are not reloaded""" - - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self._test_conf_files_mutate() - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('old_foo', self.conf.foo) - - def test_conf_files_mutate_foo(self): - """Test that a mutable opt can be reloaded.""" - - self.conf.register_cli_opt(cfg.StrOpt('foo', mutable=True)) - self._test_conf_files_mutate() - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('new_foo', self.conf.foo) - - def test_conf_files_mutate_group(self): - """Test that mutable opts in groups can be reloaded.""" - self.conf.register_cli_opt(cfg.StrOpt('boo', mutable=True), - group=self.my_group) - self._test_conf_files_mutate() - self.assertTrue(hasattr(self.conf, 'group')) - self.assertTrue(hasattr(self.conf.group, 'boo')) - self.assertEqual('new_boo', self.conf.group.boo) - - def test_warn_immutability(self): - self.log_fixture = self.useFixture(fixtures.FakeLogger()) - self.conf.register_cli_opt(cfg.StrOpt('foo', mutable=True)) - self.conf.register_cli_opt(cfg.StrOpt('boo'), group=self.my_group) - self._test_conf_files_mutate() - self.assertEqual( - "Ignoring change to immutable option group.boo\n" - "Option DEFAULT.foo changed from [old_foo] to [new_foo]\n", - self.log_fixture.output) - - def test_diff(self): - self.log_fixture = self.useFixture(fixtures.FakeLogger()) - self.conf.register_cli_opt(cfg.StrOpt('imm')) - self.conf.register_cli_opt(cfg.StrOpt('blank', mutable=True)) - self.conf.register_cli_opt(cfg.StrOpt('foo', mutable=True)) - self.conf.register_cli_opt(cfg.StrOpt('boo', mutable=True), - group=self.my_group) - diff = self._test_conf_files_mutate() - self.assertEqual( - {(None, 'foo'): ('old_foo', 'new_foo'), - ('group', 'boo'): ('old_boo', 'new_boo')}, - diff) - expected = ("Option DEFAULT.foo changed from [old_foo] to [new_foo]\n" - "Option group.boo changed from [old_boo] to [new_boo]\n") - self.assertEqual(expected, self.log_fixture.output) - - def test_hooks_invoked_once(self): - fresh = {} - result = [0] - - def foo(conf, foo_fresh): - self.assertEqual(conf, self.conf) - self.assertEqual(fresh, foo_fresh) - result[0] += 1 - - self.conf.register_mutate_hook(foo) - self.conf.register_mutate_hook(foo) - self._test_conf_files_mutate() - self.assertEqual(1, result[0]) - - def test_hooks_see_new_values(self): - def foo(conf, fresh): - # Verify that we see the new value inside the mutate hook. - self.assertEqual('new_foo', conf.foo) - - self.conf.register_cli_opt(cfg.StrOpt('foo', mutable=True)) - self.conf.register_mutate_hook(foo) - - paths = self.create_tempfiles([ - ('1', '[DEFAULT]\n' - 'foo = old_foo\n' - '[group]\n' - 'boo = old_boo\n'), - ('2', '[DEFAULT]\n' - 'foo = new_foo\n' - '[group]\n' - 'boo = new_boo\n')]) - - self.conf(['--config-file', paths[0]]) - # We access the value once before mutating it to ensure the - # cache is populated. - self.assertEqual('old_foo', self.conf.foo) - - shutil.copy(paths[1], paths[0]) - self.conf.mutate_config_files() - # Verify that we see the new value after mutation is complete. - self.assertEqual('new_foo', self.conf.foo) - - def test_clear(self): - """Show that #clear doesn't undeclare opts. - - This justifies not clearing mutate_hooks either. ResetAndClearTestCase - shows that values are cleared. - """ - self.conf.register_cli_opt(cfg.StrOpt('cli')) - self.conf.register_opt(cfg.StrOpt('foo')) - dests = [info['opt'].dest for info, _ in self.conf._all_opt_infos()] - self.assertIn('cli', dests) - self.assertIn('foo', dests) - - self.conf.clear() - dests = [info['opt'].dest for info, _ in self.conf._all_opt_infos()] - self.assertIn('cli', dests) - self.assertIn('foo', dests) - - -class OptGroupsTestCase(BaseTestCase): - - def test_arg_group(self): - blaa_group = cfg.OptGroup('blaa', 'blaa options') - self.conf.register_group(blaa_group) - self.conf.register_cli_opt(cfg.StrOpt('foo'), group=blaa_group) - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_autocreate_group_by_name(self): - self.conf.register_cli_opt(cfg.StrOpt('foo'), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_autocreate_group_by_group(self): - group = cfg.OptGroup(name='blaa', title='Blaa options') - self.conf.register_cli_opt(cfg.StrOpt('foo'), group=group) - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_autocreate_title(self): - blaa_group = cfg.OptGroup('blaa') - self.assertEqual(blaa_group.title, 'blaa options') - - def test_arg_group_by_name(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt(cfg.StrOpt('foo'), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_with_default(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt( - cfg.StrOpt('foo', default='bar'), group='blaa') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_with_conf_and_group_opts(self): - self.conf.register_cli_opt(cfg.StrOpt('conf'), group='blaa') - self.conf.register_cli_opt(cfg.StrOpt('group'), group='blaa') - - self.conf(['--blaa-conf', 'foo', '--blaa-group', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'conf')) - self.assertEqual('foo', self.conf.blaa.conf) - self.assertTrue(hasattr(self.conf.blaa, 'group')) - self.assertEqual('bar', self.conf.blaa.group) - - def test_arg_group_in_config_file(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo'), group='blaa') - - paths = self.create_tempfiles([('test', - '[blaa]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_with_deprecated_name(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', deprecated_name='oldfoo'), - group='blaa') - - paths = self.create_tempfiles([('test', - '[blaa]\n' - 'oldfoo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_with_deprecated_group(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', deprecated_group='DEFAULT'), - group='blaa') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_with_deprecated_group_and_name(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt( - cfg.StrOpt('foo', deprecated_group='DEFAULT', - deprecated_name='oldfoo'), group='blaa') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'oldfoo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_override_deprecated_name(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', deprecated_name='oldfoo'), - group='blaa') - - paths = self.create_tempfiles([('test', - '[blaa]\n' - 'foo = bar\n' - 'oldfoo = blabla\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_override_deprecated_group(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', deprecated_group='DEFAULT'), - group='blaa') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = blabla\n' - '[blaa]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_override_deprecated_group_and_name(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt( - cfg.StrOpt('foo', deprecated_group='DEFAULT', - deprecated_name='oldfoo'), group='blaa') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'oldfoo = blabla\n' - '[blaa]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_with_capital_name(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo'), group='blaa') - - paths = self.create_tempfiles([('test', - '[BLAA]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertFalse(hasattr(self.conf, 'BLAA')) - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_arg_group_in_config_file_with_capital_name_on_legacy_code(self): - self.conf.register_group(cfg.OptGroup('BLAA')) - self.conf.register_opt(cfg.StrOpt('foo'), group='BLAA') - - paths = self.create_tempfiles([('test', - '[BLAA]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertFalse(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf, 'BLAA')) - self.assertTrue(hasattr(self.conf.BLAA, 'foo')) - self.assertEqual('bar', self.conf.BLAA.foo) - - -class MappingInterfaceTestCase(BaseTestCase): - - def test_mapping_interface(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - - self.conf(['--foo', 'bar']) - - self.assertTrue('foo' in self.conf) - self.assertTrue('config_file' in self.conf) - self.assertEqual(len(self.conf), 3) - self.assertEqual('bar', self.conf['foo']) - self.assertEqual('bar', self.conf.get('foo')) - self.assertTrue('bar' in list(self.conf.values())) - - def test_mapping_interface_with_group(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt(cfg.StrOpt('foo'), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue('blaa' in self.conf) - self.assertTrue('foo' in list(self.conf['blaa'])) - self.assertEqual(len(self.conf['blaa']), 1) - self.assertEqual('bar', self.conf['blaa']['foo']) - self.assertEqual('bar', self.conf['blaa'].get('foo')) - self.assertTrue('bar' in self.conf['blaa'].values()) - self.assertEqual(self.conf['blaa'], self.conf.blaa) - - -class OptNameSeparatorTestCast(BaseTestCase): - - scenarios = [ - ('hyphen', - dict(opt_name='foo-bar', - opt_dest='foo_bar', - broken_opt_dest='foo-bar', - cf_name='foo_bar', - broken_cf_name='foo-bar', - cli_name='foo-bar', - broken_cli_name='foo_bar', - broken=True)), # FIXME(markmc): see #1279973 - ('underscore', - dict(opt_name='foo_bar', - opt_dest='foo_bar', - broken_opt_dest='foo-bar', - cf_name='foo_bar', - broken_cf_name='foo-bar', - cli_name='foo_bar', - broken_cli_name='foo_bar', - broken=False)), - ] - - def test_attribute_and_key_name(self): - self.conf.register_opt(cfg.StrOpt(self.opt_name)) - - self.assertTrue(hasattr(self.conf, self.opt_dest)) - self.assertFalse(hasattr(self.conf, self.broken_opt_dest)) - self.assertIn(self.opt_dest, self.conf) - self.assertNotIn(self.broken_opt_dest, self.conf) - - def test_cli_opt_name(self): - self.conf.register_cli_opt(cfg.BoolOpt(self.opt_name)) - - self.conf(['--' + self.cli_name]) - - self.assertTrue(getattr(self.conf, self.opt_dest)) - - def test_config_file_opt_name(self): - self.conf.register_opt(cfg.BoolOpt(self.opt_name)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' + - self.cf_name + ' = True\n' + - self.broken_cf_name + ' = False\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(getattr(self.conf, self.opt_dest)) - - def test_deprecated_name(self): - self.conf.register_opt(cfg.StrOpt('foobar', - deprecated_name=self.opt_name)) - - self.assertTrue(hasattr(self.conf, 'foobar')) - self.assertFalse(hasattr(self.conf, self.opt_dest)) - self.assertFalse(hasattr(self.conf, self.broken_opt_dest)) - self.assertIn('foobar', self.conf) - self.assertNotIn(self.opt_dest, self.conf) - self.assertNotIn(self.broken_opt_dest, self.conf) - - def test_deprecated_name_cli(self): - self.conf.register_cli_opt(cfg.BoolOpt('foobar', - deprecated_name=self.opt_name)) - - # FIXME(markmc): this should be self.cli_name, see #1279973 - if self.broken: - self.conf(['--' + self.broken_cli_name]) - else: - self.conf(['--' + self.cli_name]) - - self.assertTrue(self.conf.foobar) - - def test_deprecated_name_config_file(self): - self.conf.register_opt(cfg.BoolOpt('foobar', - deprecated_name=self.opt_name)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' + - self.cf_name + ' = True\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(self.conf.foobar) - - def test_deprecated_opts(self): - oldopts = [cfg.DeprecatedOpt(self.opt_name)] - self.conf.register_opt(cfg.StrOpt('foobar', - deprecated_opts=oldopts)) - - self.assertTrue(hasattr(self.conf, 'foobar')) - self.assertFalse(hasattr(self.conf, self.opt_dest)) - self.assertFalse(hasattr(self.conf, self.broken_opt_dest)) - self.assertIn('foobar', self.conf) - self.assertNotIn(self.opt_dest, self.conf) - self.assertNotIn(self.broken_opt_dest, self.conf) - - def test_deprecated_opts_cli(self): - oldopts = [cfg.DeprecatedOpt(self.opt_name)] - self.conf.register_cli_opt(cfg.BoolOpt('foobar', - deprecated_opts=oldopts)) - - self.conf(['--' + self.cli_name]) - - self.assertTrue(self.conf.foobar) - - def test_deprecated_opts_config_file(self): - oldopts = [cfg.DeprecatedOpt(self.opt_name)] - self.conf.register_opt(cfg.BoolOpt('foobar', - deprecated_opts=oldopts)) - - # FIXME(markmc): this should be self.cf_name, see #1279973 - if self.broken: - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' + - self.broken_cf_name + - ' = True\n')]) - else: - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' + - self.cf_name + ' = True\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(self.conf.foobar) - - -class ReRegisterOptTestCase(BaseTestCase): - - def test_conf_file_re_register_opt(self): - opt = cfg.StrOpt('foo') - self.assertTrue(self.conf.register_opt(opt)) - self.assertFalse(self.conf.register_opt(opt)) - - def test_conf_file_re_register_opt_in_group(self): - group = cfg.OptGroup('blaa') - self.conf.register_group(group) - self.conf.register_group(group) # not an error - opt = cfg.StrOpt('foo') - self.assertTrue(self.conf.register_opt(opt, group=group)) - self.assertFalse(self.conf.register_opt(opt, group='blaa')) - - -class RegisterOptNameTestCase(BaseTestCase): - - def test_register_opt_with_disallow_name(self): - for name in cfg.ConfigOpts.disallow_names: - opt = cfg.StrOpt(name) - self.assertRaises(ValueError, self.conf.register_opt, opt) - - -class TemplateSubstitutionTestCase(BaseTestCase): - - def _prep_test_str_sub(self, foo_default=None, bar_default=None): - self.conf.register_cli_opt(cfg.StrOpt('foo', default=foo_default)) - self.conf.register_cli_opt(cfg.StrOpt('bar', default=bar_default)) - - def _assert_str_sub(self): - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual('blaa', self.conf.bar) - - def test_str_sub_default_from_default(self): - self._prep_test_str_sub(foo_default='blaa', bar_default='$foo') - - self.conf([]) - - self._assert_str_sub() - - def test_str_sub_default_from_default_recurse(self): - self.conf.register_cli_opt(cfg.StrOpt('blaa', default='blaa')) - self._prep_test_str_sub(foo_default='$blaa', bar_default='$foo') - - self.conf([]) - - self._assert_str_sub() - - def test_str_sub_default_from_arg(self): - self._prep_test_str_sub(bar_default='$foo') - - self.conf(['--foo', 'blaa']) - - self._assert_str_sub() - - def test_str_sub_default_from_config_file(self): - self._prep_test_str_sub(bar_default='$foo') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_arg_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - self.conf(['--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_arg(self): - self._prep_test_str_sub() - - self.conf(['--foo', 'blaa', '--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0], '--bar=$foo']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_config_file_from_arg(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0], '--foo=blaa']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar = $foo\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_with_dollar_escape_char(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar=foo-somethin$$k2\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual('foo-somethin$k2', self.conf.bar) - - def test_str_sub_with_backslash_escape_char(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar=foo-somethin\$k2\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual('foo-somethin$k2', self.conf.bar) - - def test_str_sub_group_from_default(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default='blaa')) - self.conf.register_group(cfg.OptGroup('ba')) - self.conf.register_cli_opt(cfg.StrOpt('r', default='$foo'), group='ba') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEqual('blaa', self.conf.ba.r) - - def test_str_sub_set_default(self): - self._prep_test_str_sub() - self.conf.set_default('bar', '$foo') - self.conf.set_default('foo', 'blaa') - - self.conf([]) - - self._assert_str_sub() - - def test_str_sub_set_override(self): - self._prep_test_str_sub() - self.conf.set_override('bar', '$foo') - self.conf.set_override('foo', 'blaa') - - self.conf([]) - - self._assert_str_sub() - - def _prep_test_str_int_sub(self, foo_default=None, bar_default=None): - self.conf.register_cli_opt(cfg.StrOpt('foo', default=foo_default)) - self.conf.register_cli_opt(cfg.IntOpt('bar', default=bar_default)) - - def _assert_int_sub(self): - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual(123, self.conf.bar) - - def test_sub_default_from_default(self): - self._prep_test_str_int_sub(foo_default='123', bar_default='$foo') - - self.conf([]) - - self._assert_int_sub() - - def test_sub_default_from_default_recurse(self): - self.conf.register_cli_opt(cfg.StrOpt('blaa', default='123')) - self._prep_test_str_int_sub(foo_default='$blaa', bar_default='$foo') - - self.conf([]) - - self._assert_int_sub() - - def test_sub_default_from_arg(self): - self._prep_test_str_int_sub(bar_default='$foo') - - self.conf(['--foo', '123']) - - self._assert_int_sub() - - def test_sub_default_from_config_file(self): - self._prep_test_str_int_sub(bar_default='$foo') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 123\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_int_sub() - - def test_sub_arg_from_default(self): - self._prep_test_str_int_sub(foo_default='123') - - self.conf(['--bar', '$foo']) - - self._assert_int_sub() - - def test_sub_arg_from_arg(self): - self._prep_test_str_int_sub() - - self.conf(['--foo', '123', '--bar', '$foo']) - - self._assert_int_sub() - - def test_sub_arg_from_config_file(self): - self._prep_test_str_int_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = 123\n')]) - - self.conf(['--config-file', paths[0], '--bar=$foo']) - - self._assert_int_sub() - - def test_sub_config_file_from_default(self): - self._prep_test_str_int_sub(foo_default='123') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_int_sub() - - def test_sub_config_file_from_arg(self): - self._prep_test_str_int_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0], '--foo=123']) - - self._assert_int_sub() - - def test_sub_config_file_from_config_file(self): - self._prep_test_str_int_sub() - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'bar = $foo\n' - 'foo = 123\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_int_sub() - - def test_sub_group_from_default(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default='123')) - self.conf.register_group(cfg.OptGroup('ba')) - self.conf.register_cli_opt(cfg.IntOpt('r', default='$foo'), group='ba') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEqual('123', self.conf.foo) - self.assertEqual(123, self.conf.ba.r) - - def test_sub_group_from_default_deprecated(self): - self.conf.register_group(cfg.OptGroup('ba')) - self.conf.register_cli_opt(cfg.StrOpt( - 'foo', default='123', deprecated_group='DEFAULT'), group='ba') - self.conf.register_cli_opt(cfg.IntOpt('r', default='$foo'), group='ba') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'foo')) - self.assertEqual('123', self.conf.ba.foo) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEqual(123, self.conf.ba.r) - - def test_sub_group_from_args_deprecated(self): - self.conf.register_group(cfg.OptGroup('ba')) - self.conf.register_cli_opt(cfg.StrOpt( - 'foo', default='123', deprecated_group='DEFAULT'), group='ba') - self.conf.register_cli_opt(cfg.IntOpt('r', default='$foo'), group='ba') - - self.conf(['--ba-foo=4242']) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'foo')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEqual('4242', self.conf.ba.foo) - self.assertEqual(4242, self.conf.ba.r) - - def test_sub_group_from_configfile_deprecated(self): - self.conf.register_group(cfg.OptGroup('ba')) - self.conf.register_cli_opt(cfg.StrOpt( - 'foo', default='123', deprecated_group='DEFAULT'), group='ba') - self.conf.register_cli_opt(cfg.IntOpt('r', default='$foo'), group='ba') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo=4242\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'foo')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEqual('4242', self.conf.ba.foo) - self.assertEqual(4242, self.conf.ba.r) - - def test_dict_sub_default_from_default(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default='floo')) - self.conf.register_cli_opt(cfg.StrOpt('bar', default='blaa')) - self.conf.register_cli_opt(cfg.DictOpt('dt', default={'$foo': '$bar'})) - - self.conf([]) - - self.assertEqual('blaa', self.conf.dt['floo']) - - def test_dict_sub_default_from_default_multi(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default='floo')) - self.conf.register_cli_opt(cfg.StrOpt('bar', default='blaa')) - self.conf.register_cli_opt(cfg.StrOpt('goo', default='gloo')) - self.conf.register_cli_opt(cfg.StrOpt('har', default='hlaa')) - self.conf.register_cli_opt(cfg.DictOpt('dt', default={'$foo': '$bar', - '$goo': 'goo', - 'har': '$har', - 'key1': 'str', - 'key2': 12345})) - - self.conf([]) - - self.assertEqual('blaa', self.conf.dt['floo']) - self.assertEqual('goo', self.conf.dt['gloo']) - self.assertEqual('hlaa', self.conf.dt['har']) - self.assertEqual('str', self.conf.dt['key1']) - self.assertEqual(12345, self.conf.dt['key2']) - - def test_dict_sub_default_from_default_recurse(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default='$foo2')) - self.conf.register_cli_opt(cfg.StrOpt('foo2', default='floo')) - self.conf.register_cli_opt(cfg.StrOpt('bar', default='$bar2')) - self.conf.register_cli_opt(cfg.StrOpt('bar2', default='blaa')) - self.conf.register_cli_opt(cfg.DictOpt('dt', default={'$foo': '$bar'})) - - self.conf([]) - - self.assertEqual('blaa', self.conf.dt['floo']) - - def test_dict_sub_default_from_arg(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default=None)) - self.conf.register_cli_opt(cfg.StrOpt('bar', default=None)) - self.conf.register_cli_opt(cfg.DictOpt('dt', default={'$foo': '$bar'})) - - self.conf(['--foo', 'floo', '--bar', 'blaa']) - - self.assertTrue(hasattr(self.conf, 'dt')) - self.assertEqual('blaa', self.conf.dt['floo']) - - def test_dict_sub_default_from_config_file(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', default='floo')) - self.conf.register_cli_opt(cfg.StrOpt('bar', default='blaa')) - self.conf.register_cli_opt(cfg.DictOpt('dt', default={})) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'dt = $foo:$bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'dt')) - self.assertEqual('blaa', self.conf.dt['floo']) - - -class ConfigDirTestCase(BaseTestCase): - - def test_config_dir(self): - snafu_group = cfg.OptGroup('snafu') - self.conf.register_group(snafu_group) - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.conf.register_cli_opt(cfg.StrOpt('bell'), group=snafu_group) - - dir = tempfile.mkdtemp() - self.tempdirs.append(dir) - - paths = self.create_tempfiles([(os.path.join(dir, '00-test'), - '[DEFAULT]\n' - 'foo = bar-00\n' - '[snafu]\n' - 'bell = whistle-00\n'), - (os.path.join(dir, '02-test'), - '[snafu]\n' - 'bell = whistle-02\n' - '[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-02', self.conf.foo) - self.assertTrue(hasattr(self.conf, 'snafu')) - self.assertTrue(hasattr(self.conf.snafu, 'bell')) - self.assertEqual('whistle-02', self.conf.snafu.bell) - - 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) - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.conf.register_cli_opt(cfg.StrOpt('bell'), group=snafu_group) - - dir = tempfile.mkdtemp() - self.tempdirs.append(dir) - - paths = self.create_tempfiles([(os.path.join(dir, '00-test'), - '[DEFAULT]\n' - 'foo = bar-00\n'), - ('01-test', - '[snafu]\n' - 'bell = whistle-01\n' - '[DEFAULT]\n' - 'foo = bar-01\n'), - ('03-test', - '[snafu]\n' - 'bell = whistle-03\n' - '[DEFAULT]\n' - 'foo = bar-03\n'), - (os.path.join(dir, '02-test'), - '[DEFAULT]\n' - 'foo = bar-02\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[1], - '--config-dir', os.path.dirname(paths[0]), - '--config-file', paths[2], ]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar-03', self.conf.foo) - self.assertTrue(hasattr(self.conf, 'snafu')) - self.assertTrue(hasattr(self.conf.snafu, 'bell')) - self.assertEqual('whistle-03', self.conf.snafu.bell) - - def test_config_dir_default_file_precedence(self): - snafu_group = cfg.OptGroup('snafu') - self.conf.register_group(snafu_group) - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.conf.register_cli_opt(cfg.StrOpt('bell'), group=snafu_group) - - dir = tempfile.mkdtemp() - self.tempdirs.append(dir) - - paths = self.create_tempfiles([(os.path.join(dir, '00-test'), - '[DEFAULT]\n' - 'foo = bar-00\n' - '[snafu]\n' - 'bell = whistle-11\n'), - ('01-test', - '[snafu]\n' - 'bell = whistle-01\n' - '[DEFAULT]\n' - 'foo = bar-01\n'), - ('03-test', - '[snafu]\n' - 'bell = whistle-03\n' - '[DEFAULT]\n' - 'foo = bar-03\n'), - (os.path.join(dir, '02-test'), - '[DEFAULT]\n' - 'foo = bar-02\n')]) - - self.conf(['--foo', 'bar', '--config-dir', os.path.dirname(paths[0])], - default_config_files=[paths[1], paths[2]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar-02', self.conf.foo) - self.assertTrue(hasattr(self.conf, 'snafu')) - self.assertTrue(hasattr(self.conf.snafu, 'bell')) - self.assertEqual('whistle-11', self.conf.snafu.bell) - - def test_config_dir_doesnt_exist(self): - tmpdir = '/tmp/foo' - - self.assertRaises(cfg.ConfigDirNotFoundError, - self.conf, - ['--config-dir', tmpdir] - ) - - -class ReparseTestCase(BaseTestCase): - - def test_reparse(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt( - cfg.StrOpt('foo', default='r'), group='blaa') - - paths = self.create_tempfiles([('test', - '[blaa]\n' - 'foo = b\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('b', self.conf.blaa.foo) - - self.conf(['--blaa-foo', 'a']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('a', self.conf.blaa.foo) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('r', self.conf.blaa.foo) - - -class OverridesTestCase(BaseTestCase): - - def test_default_none(self): - self.conf.register_opt(cfg.StrOpt('foo', default='foo')) - self.conf([]) - self.assertEqual('foo', self.conf.foo) - self.conf.set_default('foo', None) - self.assertIsNone(self.conf.foo) - self.conf.clear_default('foo') - self.assertEqual('foo', self.conf.foo) - - def test_no_default_override(self): - self.conf.register_opt(cfg.StrOpt('foo')) - self.conf([]) - self.assertIsNone(self.conf.foo) - self.conf.set_default('foo', 'bar') - self.assertEqual('bar', self.conf.foo) - self.conf.clear_default('foo') - self.assertIsNone(self.conf.foo) - - def test_default_override(self): - self.conf.register_opt(cfg.StrOpt('foo', default='foo')) - self.conf([]) - self.assertEqual('foo', self.conf.foo) - self.conf.set_default('foo', 'bar') - self.assertEqual('bar', self.conf.foo) - self.conf.clear_default('foo') - self.assertEqual('foo', self.conf.foo) - - def test_set_default_not_in_choices(self): - self.conf.register_group(cfg.OptGroup('f')) - self.conf.register_cli_opt(cfg.StrOpt('oo', choices=('a', 'b')), - group='f') - self.assertRaises(ValueError, - self.conf.set_default, 'oo', 'c', 'f', - enforce_type=True) - - def test_enforce_type_default_override(self): - self.conf.register_opt(cfg.StrOpt('foo', default='foo')) - self.conf([]) - self.assertEqual('foo', self.conf.foo) - self.conf.set_default('foo', 'bar', enforce_type=True) - self.assertEqual('bar', self.conf.foo) - self.conf.clear_default('foo') - self.assertEqual('foo', self.conf.foo) - - def test_override(self): - self.conf.register_opt(cfg.StrOpt('foo')) - self.conf.set_override('foo', 'bar') - self.conf([]) - self.assertEqual('bar', self.conf.foo) - self.conf.clear_override('foo') - self.assertIsNone(self.conf.foo) - - def test_override_none(self): - self.conf.register_opt(cfg.StrOpt('foo', default='foo')) - self.conf([]) - self.assertEqual('foo', self.conf.foo) - self.conf.set_override('foo', None) - self.assertIsNone(self.conf.foo) - self.conf.clear_override('foo') - self.assertEqual('foo', self.conf.foo) - - def test_group_no_default_override(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo'), group='blaa') - self.conf([]) - self.assertIsNone(self.conf.blaa.foo) - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEqual('bar', self.conf.blaa.foo) - self.conf.clear_default('foo', group='blaa') - self.assertIsNone(self.conf.blaa.foo) - - def test_group_default_override(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', default='foo'), group='blaa') - self.conf([]) - self.assertEqual('foo', self.conf.blaa.foo) - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEqual('bar', self.conf.blaa.foo) - self.conf.clear_default('foo', group='blaa') - self.assertEqual('foo', self.conf.blaa.foo) - - def test_group_override(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo'), group='blaa') - self.assertIsNone(self.conf.blaa.foo) - self.conf.set_override('foo', 'bar', group='blaa') - self.conf([]) - self.assertEqual('bar', self.conf.blaa.foo) - self.conf.clear_override('foo', group='blaa') - self.assertIsNone(self.conf.blaa.foo) - - def test_cli_bool_default(self): - self.conf.register_cli_opt(cfg.BoolOpt('foo')) - self.conf.set_default('foo', True) - self.assertTrue(self.conf.foo) - self.conf([]) - self.assertTrue(self.conf.foo) - self.conf.set_default('foo', False) - self.assertFalse(self.conf.foo) - self.conf.clear_default('foo') - self.assertIsNone(self.conf.foo) - - def test_cli_bool_override(self): - self.conf.register_cli_opt(cfg.BoolOpt('foo')) - self.conf.set_override('foo', True) - self.assertTrue(self.conf.foo) - self.conf([]) - self.assertTrue(self.conf.foo) - self.conf.set_override('foo', False) - self.assertFalse(self.conf.foo) - self.conf.clear_override('foo') - self.assertIsNone(self.conf.foo) - - def test_enforce_type_str_override(self): - self.conf.register_opt(cfg.StrOpt('foo')) - self.conf.set_override('foo', True, enforce_type=True) - self.conf([]) - self.assertEqual('True', self.conf.foo) - self.conf.clear_override('foo') - self.assertIsNone(self.conf.foo) - - def test_set_override_in_choices(self): - self.conf.register_group(cfg.OptGroup('f')) - self.conf.register_cli_opt(cfg.StrOpt('oo', choices=('a', 'b')), - group='f') - self.conf.set_override('oo', 'b', 'f', enforce_type=True) - self.assertEqual('b', self.conf.f.oo) - - def test_set_override_not_in_choices(self): - self.conf.register_group(cfg.OptGroup('f')) - self.conf.register_cli_opt(cfg.StrOpt('oo', choices=('a', 'b')), - group='f') - self.assertRaises(ValueError, - self.conf.set_override, 'oo', 'c', 'f', - enforce_type=True) - - def test_enforce_type_bool_override(self): - self.conf.register_opt(cfg.BoolOpt('foo')) - self.conf.set_override('foo', 'True', enforce_type=True) - self.conf([]) - self.assertTrue(self.conf.foo) - self.conf.clear_override('foo') - self.assertIsNone(self.conf.foo) - - def test_enforce_type_int_override_with_None(self): - self.conf.register_opt(cfg.IntOpt('foo')) - self.conf.set_override('foo', None, enforce_type=True) - self.conf([]) - self.assertIsNone(self.conf.foo) - self.conf.clear_override('foo') - self.assertIsNone(self.conf.foo) - - def test_enforce_type_str_override_with_None(self): - self.conf.register_opt(cfg.StrOpt('foo')) - self.conf.set_override('foo', None, enforce_type=True) - self.conf([]) - self.assertIsNone(self.conf.foo) - self.conf.clear_override('foo') - self.assertIsNone(self.conf.foo) - - -class ResetAndClearTestCase(BaseTestCase): - - def test_clear(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.conf.register_cli_opt(cfg.StrOpt('bar'), group='blaa') - - self.assertIsNone(self.conf.foo) - self.assertIsNone(self.conf.blaa.bar) - - self.conf(['--foo', 'foo', '--blaa-bar', 'bar']) - - self.assertEqual('foo', self.conf.foo) - self.assertEqual('bar', self.conf.blaa.bar) - - self.conf.clear() - - self.assertIsNone(self.conf.foo) - self.assertIsNone(self.conf.blaa.bar) - - def test_reset_and_clear_with_defaults_and_overrides(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.conf.register_cli_opt(cfg.StrOpt('bar'), group='blaa') - - self.conf.set_default('foo', 'foo') - self.conf.set_override('bar', 'bar', group='blaa') - - self.conf(['--foo', 'foofoo']) - - self.assertEqual('foofoo', self.conf.foo) - self.assertEqual('bar', self.conf.blaa.bar) - - self.conf.clear() - - self.assertEqual('foo', self.conf.foo) - self.assertEqual('bar', self.conf.blaa.bar) - - self.conf.reset() - - self.assertIsNone(self.conf.foo) - self.assertIsNone(self.conf.blaa.bar) - - -class UnregisterOptTestCase(BaseTestCase): - - def test_unregister_opt(self): - opts = [cfg.StrOpt('foo'), cfg.StrOpt('bar')] - - self.conf.register_opts(opts) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertTrue(hasattr(self.conf, 'bar')) - - self.conf.unregister_opt(opts[0]) - - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertTrue(hasattr(self.conf, 'bar')) - - self.conf([]) - - self.assertRaises(cfg.ArgsAlreadyParsedError, - self.conf.unregister_opt, opts[1]) - - self.conf.clear() - - self.assertTrue(hasattr(self.conf, 'bar')) - - self.conf.unregister_opts(opts) - - def test_unregister_opt_from_group(self): - opt = cfg.StrOpt('foo') - - self.conf.register_opt(opt, group='blaa') - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - - self.conf.unregister_opt(opt, group='blaa') - - self.assertFalse(hasattr(self.conf.blaa, 'foo')) - - -class ImportOptTestCase(BaseTestCase): - - def test_import_opt(self): - self.assertFalse(hasattr(cfg.CONF, 'blaa')) - cfg.CONF.import_opt('blaa', 'oslo_config.tests.testmods.blaa_opt') - self.assertTrue(hasattr(cfg.CONF, 'blaa')) - - def test_import_opt_in_group(self): - self.assertFalse(hasattr(cfg.CONF, 'bar')) - cfg.CONF.import_opt('foo', 'oslo_config.tests.testmods.bar_foo_opt', - group='bar') - self.assertTrue(hasattr(cfg.CONF, 'bar')) - self.assertTrue(hasattr(cfg.CONF.bar, 'foo')) - - def test_import_opt_import_errror(self): - self.assertRaises(ImportError, cfg.CONF.import_opt, - 'blaa', 'oslo_config.tests.testmods.blaablaa_opt') - - def test_import_opt_no_such_opt(self): - self.assertRaises(cfg.NoSuchOptError, cfg.CONF.import_opt, - 'blaablaa', 'oslo_config.tests.testmods.blaa_opt') - - def test_import_opt_no_such_group(self): - self.assertRaises(cfg.NoSuchGroupError, cfg.CONF.import_opt, - 'blaa', 'oslo_config.tests.testmods.blaa_opt', - group='blaa') - - -class ImportGroupTestCase(BaseTestCase): - - def test_import_group(self): - self.assertFalse(hasattr(cfg.CONF, 'qux')) - cfg.CONF.import_group('qux', 'oslo_config.tests.testmods.baz_qux_opt') - self.assertTrue(hasattr(cfg.CONF, 'qux')) - self.assertTrue(hasattr(cfg.CONF.qux, 'baz')) - - def test_import_group_import_error(self): - self.assertRaises(ImportError, cfg.CONF.import_group, - 'qux', 'oslo_config.tests.testmods.bazzz_quxxx_opt') - - def test_import_group_no_such_group(self): - self.assertRaises(cfg.NoSuchGroupError, cfg.CONF.import_group, - 'quxxx', 'oslo_config.tests.testmods.baz_qux_opt') - - -class RequiredOptsTestCase(BaseTestCase): - - def setUp(self): - BaseTestCase.setUp(self) - self.conf.register_opt(cfg.StrOpt('boo', required=False)) - - def test_required_opt(self): - self.conf.register_opt(cfg.StrOpt('foo', required=True)) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - def test_required_cli_opt(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', required=True)) - - self.conf(['--foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - def test_required_cli_opt_with_dash(self): - self.conf.register_cli_opt(cfg.StrOpt('foo-bar', required=True)) - - self.conf(['--foo-bar', 'baz']) - - self.assertTrue(hasattr(self.conf, 'foo_bar')) - self.assertEqual('baz', self.conf.foo_bar) - - def test_missing_required_opt(self): - self.conf.register_opt(cfg.StrOpt('foo', required=True)) - self.assertRaises(cfg.RequiredOptError, self.conf, []) - - def test_missing_required_cli_opt(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', required=True)) - self.assertRaises(cfg.RequiredOptError, self.conf, []) - - def test_required_group_opt(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', required=True), group='blaa') - - paths = self.create_tempfiles([('test', - '[blaa]\n' - 'foo = bar')]) - - self.conf(['--config-file', paths[0]]) - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_required_cli_group_opt(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt( - cfg.StrOpt('foo', required=True), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_missing_required_group_opt(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', required=True), group='blaa') - self.assertRaises(cfg.RequiredOptError, self.conf, []) - - def test_missing_required_cli_group_opt(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt( - cfg.StrOpt('foo', required=True), group='blaa') - self.assertRaises(cfg.RequiredOptError, self.conf, []) - - def test_required_opt_with_default(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', required=True)) - self.conf.set_default('foo', 'bar') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - def test_required_opt_with_override(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', required=True)) - self.conf.set_override('foo', 'bar') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - -class SadPathTestCase(BaseTestCase): - - def test_unknown_attr(self): - self.conf([]) - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertRaises(AttributeError, getattr, self.conf, 'foo') - self.assertRaises(cfg.NoSuchOptError, self.conf._get, 'foo') - self.assertRaises(cfg.NoSuchOptError, self.conf.__getattr__, 'foo') - - def test_unknown_attr_is_attr_error(self): - self.conf([]) - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertRaises(AttributeError, getattr, self.conf, 'foo') - - def test_unknown_group_attr(self): - self.conf.register_group(cfg.OptGroup('blaa')) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertFalse(hasattr(self.conf.blaa, 'foo')) - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf.blaa, 'foo') - - def test_ok_duplicate(self): - opt = cfg.StrOpt('foo') - self.conf.register_cli_opt(opt) - opt2 = cfg.StrOpt('foo') - self.conf.register_cli_opt(opt2) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertIsNone(self.conf.foo) - - def test_error_duplicate(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', help='bar')) - self.assertRaises(cfg.DuplicateOptError, - self.conf.register_cli_opt, cfg.StrOpt('foo')) - - def test_error_duplicate_with_different_dest(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', dest='f')) - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.assertRaises(cfg.DuplicateOptError, self.conf, []) - - def test_error_duplicate_short(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', short='f')) - self.conf.register_cli_opt(cfg.StrOpt('bar', short='f')) - self.assertRaises(cfg.DuplicateOptError, self.conf, []) - - def test_already_parsed(self): - self.conf([]) - - self.assertRaises(cfg.ArgsAlreadyParsedError, - self.conf.register_cli_opt, cfg.StrOpt('foo')) - - def test_bad_cli_arg(self): - self.conf.register_opt(cfg.BoolOpt('foo')) - - self.useFixture(fixtures.MonkeyPatch('sys.stderr', moves.StringIO())) - - self.assertRaises(SystemExit, self.conf, ['--foo']) - - self.assertTrue('error' in sys.stderr.getvalue()) - self.assertTrue('--foo' in sys.stderr.getvalue()) - - def _do_test_bad_cli_value(self, opt_class): - self.conf.register_cli_opt(opt_class('foo')) - - self.useFixture(fixtures.MonkeyPatch('sys.stderr', moves.StringIO())) - - self.assertRaises(SystemExit, self.conf, ['--foo', 'bar']) - - self.assertTrue('foo' in sys.stderr.getvalue()) - self.assertTrue('bar' in sys.stderr.getvalue()) - - def test_bad_int_arg(self): - self._do_test_bad_cli_value(cfg.IntOpt) - - def test_bad_float_arg(self): - self._do_test_bad_cli_value(cfg.FloatOpt) - - def test_conf_file_not_found(self): - (fd, path) = tempfile.mkstemp() - - os.remove(path) - - self.assertRaises(cfg.ConfigFilesNotFoundError, - self.conf, ['--config-file', path]) - - def test_conf_file_permission_denied(self): - (fd, path) = tempfile.mkstemp() - - os.chmod(path, 0x000) - - self.assertRaises(cfg.ConfigFilesPermissionDeniedError, - self.conf, ['--config-file', path]) - os.remove(path) - - def test_conf_file_broken(self): - paths = self.create_tempfiles([('test', 'foo')]) - - self.assertRaises(cfg.ConfigFileParseError, - self.conf, ['--config-file', paths[0]]) - - def _do_test_conf_file_bad_value(self, opt_class): - self.conf.register_opt(opt_class('foo')) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertRaises(ValueError, getattr, self.conf, 'foo') - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - - def test_conf_file_bad_bool(self): - self._do_test_conf_file_bad_value(cfg.BoolOpt) - - def test_conf_file_bad_int(self): - self._do_test_conf_file_bad_value(cfg.IntOpt) - - def test_conf_file_bad_float(self): - self._do_test_conf_file_bad_value(cfg.FloatOpt) - - def test_str_sub_from_group(self): - self.conf.register_group(cfg.OptGroup('f')) - self.conf.register_cli_opt(cfg.StrOpt('oo', default='blaa'), group='f') - self.conf.register_cli_opt(cfg.StrOpt('bar', default='$f.oo')) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual("blaa", self.conf.bar) - - def test_str_sub_from_group_with_brace(self): - self.conf.register_group(cfg.OptGroup('f')) - self.conf.register_cli_opt(cfg.StrOpt('oo', default='blaa'), group='f') - self.conf.register_cli_opt(cfg.StrOpt('bar', default='${f.oo}')) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEqual("blaa", self.conf.bar) - - def test_set_default_unknown_attr(self): - self.conf([]) - self.assertRaises( - cfg.NoSuchOptError, self.conf.set_default, 'foo', 'bar') - - def test_set_default_unknown_group(self): - self.conf([]) - self.assertRaises(cfg.NoSuchGroupError, - self.conf.set_default, 'foo', 'bar', group='blaa') - - def test_set_override_unknown_attr(self): - self.conf([]) - self.assertRaises( - cfg.NoSuchOptError, self.conf.set_override, 'foo', 'bar') - - def test_set_override_unknown_group(self): - self.conf([]) - self.assertRaises(cfg.NoSuchGroupError, - self.conf.set_override, 'foo', 'bar', group='blaa') - - -class FindFileTestCase(BaseTestCase): - - def test_find_file_without_init(self): - self.assertRaises(cfg.NotInitializedError, - self.conf.find_file, 'foo.json') - - def test_find_policy_file(self): - policy_file = '/etc/policy.json' - - self.useFixture(fixtures.MonkeyPatch( - 'os.path.exists', - lambda p: p == policy_file)) - - self.conf([]) - - self.assertIsNone(self.conf.find_file('foo.json')) - self.assertEqual(policy_file, self.conf.find_file('policy.json')) - - def test_find_policy_file_with_config_file(self): - dir = tempfile.mkdtemp() - self.tempdirs.append(dir) - - paths = self.create_tempfiles([(os.path.join(dir, 'test.conf'), - '[DEFAULT]'), - (os.path.join(dir, 'policy.json'), - '{}')], - ext='') - - self.conf(['--config-file', paths[0]]) - - self.assertEqual(paths[1], self.conf.find_file('policy.json')) - - def test_find_policy_file_with_multiple_config_dirs(self): - dir1 = tempfile.mkdtemp() - self.tempdirs.append(dir1) - - dir2 = tempfile.mkdtemp() - self.tempdirs.append(dir2) - - self.conf(['--config-dir', dir1, '--config-dir', dir2]) - self.assertEqual(2, len(self.conf.config_dirs)) - self.assertEqual(dir1, self.conf.config_dirs[0]) - self.assertEqual(dir2, self.conf.config_dirs[1]) - - def test_config_dirs_empty_list_when_nothing_parsed(self): - self.assertEqual([], self.conf.config_dirs) - - def test_find_policy_file_with_config_dir(self): - dir = tempfile.mkdtemp() - self.tempdirs.append(dir) - - dir2 = tempfile.mkdtemp() - self.tempdirs.append(dir2) - - path = self.create_tempfiles([(os.path.join(dir, 'policy.json'), - '{}')], - ext='')[0] - - self.conf(['--config-dir', dir, '--config-dir', dir2]) - - self.assertEqual(path, self.conf.find_file('policy.json')) - - -class OptDumpingTestCase(BaseTestCase): - - class FakeLogger(object): - - def __init__(self, test_case, expected_lvl): - self.test_case = test_case - self.expected_lvl = expected_lvl - self.logged = [] - - def log(self, lvl, fmt, *args): - self.test_case.assertEqual(lvl, self.expected_lvl) - self.logged.append(fmt % args) - - def setUp(self): - super(OptDumpingTestCase, self).setUp() - self._args = ['--foo', 'this', '--blaa-bar', 'that', - '--blaa-key', 'admin', '--passwd', 'hush'] - - def _do_test_log_opt_values(self, args): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.conf.register_cli_opt(cfg.StrOpt('passwd', secret=True)) - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt(cfg.StrOpt('bar'), 'blaa') - self.conf.register_cli_opt(cfg.StrOpt('key', secret=True), 'blaa') - - self.conf(args) - - logger = self.FakeLogger(self, 666) - - self.conf.log_opt_values(logger, 666) - - self.assertEqual([ - "*" * 80, - "Configuration options gathered from:", - "command line args: ['--foo', 'this', '--blaa-bar', " - "'that', '--blaa-key', 'admin', '--passwd', 'hush']", - "config files: []", - "=" * 80, - "config_dir = None", - "config_file = []", - "foo = this", - "passwd = ****", - "blaa.bar = that", - "blaa.key = ****", - "*" * 80, - ], logger.logged) - - def test_log_opt_values(self): - self._do_test_log_opt_values(self._args) - - def test_log_opt_values_from_sys_argv(self): - self.useFixture(fixtures.MonkeyPatch('sys.argv', ['foo'] + self._args)) - self._do_test_log_opt_values(None) - - def test_log_opt_values_empty_config(self): - empty_conf = cfg.ConfigOpts() - - logger = self.FakeLogger(self, 666) - - empty_conf.log_opt_values(logger, 666) - self.assertEqual([ - "*" * 80, - "Configuration options gathered from:", - "command line args: None", - "config files: []", - "=" * 80, - "*" * 80, - ], logger.logged) - - -class ConfigParserTestCase(BaseTestCase): - - def test_parse_file(self): - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n')]) - - sections = {} - parser = cfg.ConfigParser(paths[0], sections) - parser.parse() - - self.assertTrue('DEFAULT' in sections) - self.assertTrue('BLAA' in sections) - self.assertEqual(sections['DEFAULT']['foo'], ['bar']) - self.assertEqual(sections['BLAA']['bar'], ['foo']) - - def test_parse_file_with_normalized(self): - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n')]) - - sections = {} - normalized = {} - parser = cfg.ConfigParser(paths[0], sections) - parser._add_normalized(normalized) - parser.parse() - - self.assertTrue('DEFAULT' in sections) - self.assertTrue('DEFAULT' in normalized) - self.assertTrue('BLAA' in sections) - self.assertTrue('blaa' in normalized) - self.assertEqual(sections['DEFAULT']['foo'], ['bar']) - self.assertEqual(normalized['DEFAULT']['foo'], ['bar']) - self.assertEqual(sections['BLAA']['bar'], ['foo']) - self.assertEqual(normalized['blaa']['bar'], ['foo']) - - def test_no_section(self): - with tempfile.NamedTemporaryFile() as tmpfile: - tmpfile.write(six.b('foo = bar')) - tmpfile.flush() - - parser = cfg.ConfigParser(tmpfile.name, {}) - self.assertRaises(cfg.ParseError, parser.parse) - - def test__parse_file_ioerror(self): - # Test that IOErrors (other than 'No such file or directory') - # are propagated. - filename = 'fake' - namespace = mock.Mock() - with mock.patch('oslo_config.cfg.ConfigParser.parse') as parse: - parse.side_effect = IOError(errno.EMFILE, filename, - 'Too many open files') - self.assertRaises(IOError, cfg.ConfigParser._parse_file, filename, - namespace) - - -class MultiConfigParserTestCase(BaseTestCase): - - def test_parse_single_file(self): - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n')]) - - parser = cfg.MultiConfigParser() - read_ok = parser.read(paths) - - self.assertEqual(read_ok, paths) - - self.assertTrue('DEFAULT' in parser.parsed[0]) - self.assertEqual(parser.parsed[0]['DEFAULT']['foo'], ['bar']) - self.assertEqual(parser.get([('DEFAULT', 'foo')]), ['bar']) - self.assertEqual(parser.get([('DEFAULT', 'foo')], multi=True), - ['bar']) - self.assertEqual(parser.get([('DEFAULT', 'foo')], multi=True), - ['bar']) - self.assertEqual(parser.get([(None, 'foo')], multi=True), - ['bar']) - self.assertEqual(parser._get([('DEFAULT', 'foo')], - multi=True, normalized=True), - ['bar']) - - self.assertTrue('BLAA' in parser.parsed[0]) - self.assertEqual(parser.parsed[0]['BLAA']['bar'], ['foo']) - self.assertEqual(parser.get([('BLAA', 'bar')]), ['foo']) - self.assertEqual(parser.get([('BLAA', 'bar')], multi=True), - ['foo']) - self.assertEqual(parser._get([('blaa', 'bar')], - multi=True, normalized=True), - ['foo']) - - def test_parse_multiple_files(self): - paths = self.create_tempfiles([('test1', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo'), - ('test2', - '[DEFAULT]\n' - 'foo = barbar\n' - '[BLAA]\n' - 'bar = foofoo\n' - '[bLAa]\n' - 'bar = foofoofoo\n')]) - - parser = cfg.MultiConfigParser() - read_ok = parser.read(paths) - - self.assertEqual(read_ok, paths) - - self.assertTrue('DEFAULT' in parser.parsed[0]) - self.assertEqual(parser.parsed[0]['DEFAULT']['foo'], ['barbar']) - self.assertTrue('DEFAULT' in parser.parsed[1]) - self.assertEqual(parser.parsed[1]['DEFAULT']['foo'], ['bar']) - self.assertEqual(parser.get([('DEFAULT', 'foo')]), ['barbar']) - self.assertEqual(parser.get([('DEFAULT', 'foo')], multi=True), - ['bar', 'barbar']) - - self.assertTrue('BLAA' in parser.parsed[0]) - self.assertTrue('bLAa' in parser.parsed[0]) - self.assertEqual(parser.parsed[0]['BLAA']['bar'], ['foofoo']) - self.assertEqual(parser.parsed[0]['bLAa']['bar'], ['foofoofoo']) - self.assertTrue('BLAA' in parser.parsed[1]) - self.assertEqual(parser.parsed[1]['BLAA']['bar'], ['foo']) - self.assertEqual(parser.get([('BLAA', 'bar')]), ['foofoo']) - self.assertEqual(parser.get([('bLAa', 'bar')]), ['foofoofoo']) - self.assertEqual(parser.get([('BLAA', 'bar')], multi=True), - ['foo', 'foofoo']) - self.assertEqual(parser._get([('BLAA', 'bar')], - multi=True, normalized=True), - ['foo', 'foofoo', 'foofoofoo']) - - -class NamespaceTestCase(BaseTestCase): - def setUp(self): - super(NamespaceTestCase, self).setUp() - self.ns = cfg._Namespace(self.conf) - - def read(self, *texts): - paths = ((str(i), t) for i, t in enumerate(texts)) - for path in self.create_tempfiles(paths): - cfg.ConfigParser._parse_file(path, self.ns) - - def assertAbsent(self, key, normalized=False): - self.assertRaises(KeyError, self.ns._get_value, [key], - normalized=normalized) - - def assertValue(self, key, expect, multi=False, normalized=False): - actual = self.ns._get_value([key], multi=multi, normalized=normalized) - self.assertEqual(actual, expect) - - def test_cli(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - key = (None, 'foo') - self.assertAbsent(key) - - self.read('[DEFAULT]\n' - 'foo = file0\n') - self.assertValue(key, 'file0') - - self.read('[DEFAULT]\n' - 'foo = file1\n') - self.assertEqual('file1', self.ns._get_cli_value([key])) - - def test_single_file(self): - self.read('[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n') - - self.assertValue(('DEFAULT', 'foo'), 'bar') - self.assertValue(('DEFAULT', 'foo'), ['bar'], multi=True) - self.assertValue(('DEFAULT', 'foo'), ['bar'], multi=True) - self.assertValue((None, 'foo'), ['bar'], multi=True) - self.assertValue(('DEFAULT', 'foo'), ['bar'], multi=True, - normalized=True) - - self.assertValue(('BLAA', 'bar'), 'foo') - self.assertValue(('BLAA', 'bar'), ['foo'], multi=True) - self.assertValue(('blaa', 'bar'), ['foo'], multi=True, - normalized=True) - - def test_multiple_files(self): - self.read('[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo', - - '[DEFAULT]\n' - 'foo = barbar\n' - '[BLAA]\n' - 'bar = foofoo\n' - '[bLAa]\n' - 'bar = foofoofoo\n') - - self.assertValue(('DEFAULT', 'foo'), 'barbar') - self.assertValue(('DEFAULT', 'foo'), ['bar', 'barbar'], multi=True) - - self.assertValue(('BLAA', 'bar'), 'foofoo') - self.assertValue(('bLAa', 'bar'), 'foofoofoo') - self.assertValue(('BLAA', 'bar'), ['foo', 'foofoo'], multi=True) - self.assertValue(('Blaa', 'bar'), ['foo', 'foofoo', 'foofoofoo'], - multi=True, normalized=True) - - def test_attrs_subparser(self): - CONF = cfg.ConfigOpts() - CONF.register_cli_opt(cfg.SubCommandOpt( - 'foo', handler=lambda sub: sub.add_parser('foo'))) - CONF(['foo']) - - def test_attrs_subparser_failure(self): - CONF = cfg.ConfigOpts() - CONF.register_cli_opt(cfg.SubCommandOpt( - 'foo', handler=lambda sub: sub.add_parser('foo'))) - self.assertRaises(SystemExit, CONF, ['foo', 'bar']) - - -class TildeExpansionTestCase(BaseTestCase): - - def test_config_file_tilde(self): - homedir = os.path.expanduser('~') - tmpfile = tempfile.mktemp(dir=homedir, prefix='cfg-', suffix='.conf') - tmpbase = os.path.basename(tmpfile) - - try: - self.conf(['--config-file', os.path.join('~', tmpbase)]) - except cfg.ConfigFilesNotFoundError as cfnfe: - self.assertTrue(homedir in str(cfnfe)) - - self.useFixture(fixtures.MonkeyPatch( - 'os.path.exists', - lambda p: p == tmpfile)) - - self.assertEqual(tmpfile, self.conf.find_file(tmpbase)) - - def test_config_dir_tilde(self): - homedir = os.path.expanduser('~') - try: - tmpdir = tempfile.mkdtemp(dir=homedir, - prefix='cfg-', - suffix='.d') - tmpfile = os.path.join(tmpdir, 'foo.conf') - - self.useFixture(fixtures.MonkeyPatch( - 'glob.glob', - lambda p: [tmpfile])) - - e = self.assertRaises(cfg.ConfigFilesNotFoundError, - self.conf, - ['--config-dir', - os.path.join('~', - os.path.basename(tmpdir))] - ) - self.assertIn(tmpdir, str(e)) - finally: - try: - shutil.rmtree(tmpdir) - except OSError as exc: - if exc.errno != 2: - raise - - -class SubCommandTestCase(BaseTestCase): - - def test_sub_command(self): - def add_parsers(subparsers): - sub = subparsers.add_parser('a') - sub.add_argument('bar', type=int) - - self.conf.register_cli_opt( - cfg.SubCommandOpt('cmd', handler=add_parsers)) - self.assertTrue(hasattr(self.conf, 'cmd')) - self.conf(['a', '10']) - self.assertTrue(hasattr(self.conf.cmd, 'name')) - self.assertTrue(hasattr(self.conf.cmd, 'bar')) - self.assertEqual('a', self.conf.cmd.name) - self.assertEqual(10, self.conf.cmd.bar) - - def test_sub_command_with_parent(self): - def add_parsers(subparsers): - parent = argparse.ArgumentParser(add_help=False) - parent.add_argument('bar', type=int) - subparsers.add_parser('a', parents=[parent]) - - self.conf.register_cli_opt( - cfg.SubCommandOpt('cmd', handler=add_parsers)) - self.assertTrue(hasattr(self.conf, 'cmd')) - self.conf(['a', '10']) - self.assertTrue(hasattr(self.conf.cmd, 'name')) - self.assertTrue(hasattr(self.conf.cmd, 'bar')) - self.assertEqual('a', self.conf.cmd.name) - self.assertEqual(10, self.conf.cmd.bar) - - def test_sub_command_with_dest(self): - def add_parsers(subparsers): - subparsers.add_parser('a') - - self.conf.register_cli_opt( - cfg.SubCommandOpt('cmd', dest='command', handler=add_parsers)) - self.assertTrue(hasattr(self.conf, 'command')) - self.conf(['a']) - self.assertEqual('a', self.conf.command.name) - - def test_sub_command_with_group(self): - def add_parsers(subparsers): - sub = subparsers.add_parser('a') - sub.add_argument('--bar', choices='XYZ') - - self.conf.register_cli_opt( - cfg.SubCommandOpt('cmd', handler=add_parsers), group='blaa') - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'cmd')) - self.conf(['a', '--bar', 'Z']) - self.assertTrue(hasattr(self.conf.blaa.cmd, 'name')) - self.assertTrue(hasattr(self.conf.blaa.cmd, 'bar')) - self.assertEqual('a', self.conf.blaa.cmd.name) - self.assertEqual('Z', self.conf.blaa.cmd.bar) - - def test_sub_command_not_cli(self): - self.conf.register_opt(cfg.SubCommandOpt('cmd')) - self.conf([]) - - def test_sub_command_resparse(self): - def add_parsers(subparsers): - subparsers.add_parser('a') - - self.conf.register_cli_opt( - cfg.SubCommandOpt('cmd', handler=add_parsers)) - - foo_opt = cfg.StrOpt('foo') - self.conf.register_cli_opt(foo_opt) - - self.conf(['--foo=bar', 'a']) - - self.assertTrue(hasattr(self.conf.cmd, 'name')) - self.assertEqual('a', self.conf.cmd.name) - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - - self.conf.clear() - self.conf.unregister_opt(foo_opt) - self.conf(['a']) - - self.assertTrue(hasattr(self.conf.cmd, 'name')) - self.assertEqual('a', self.conf.cmd.name) - self.assertFalse(hasattr(self.conf, 'foo')) - - def test_sub_command_no_handler(self): - self.conf.register_cli_opt(cfg.SubCommandOpt('cmd')) - self.useFixture(fixtures.MonkeyPatch('sys.stderr', moves.StringIO())) - self.assertRaises(SystemExit, self.conf, []) - self.assertTrue('error' in sys.stderr.getvalue()) - - def test_sub_command_with_help(self): - def add_parsers(subparsers): - subparsers.add_parser('a') - - self.conf.register_cli_opt(cfg.SubCommandOpt('cmd', - title='foo foo', - description='bar bar', - help='blaa blaa', - handler=add_parsers)) - self.useFixture(fixtures.MonkeyPatch('sys.stdout', moves.StringIO())) - self.assertRaises(SystemExit, self.conf, ['--help']) - self.assertTrue('foo foo' in sys.stdout.getvalue()) - self.assertTrue('bar bar' in sys.stdout.getvalue()) - self.assertTrue('blaa blaa' in sys.stdout.getvalue()) - - def test_sub_command_errors(self): - def add_parsers(subparsers): - sub = subparsers.add_parser('a') - sub.add_argument('--bar') - - self.conf.register_cli_opt(cfg.BoolOpt('bar')) - self.conf.register_cli_opt( - cfg.SubCommandOpt('cmd', handler=add_parsers)) - self.conf(['a']) - self.assertRaises(cfg.DuplicateOptError, getattr, self.conf.cmd, 'bar') - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf.cmd, 'foo') - - def test_sub_command_multiple(self): - self.conf.register_cli_opt(cfg.SubCommandOpt('cmd1')) - self.conf.register_cli_opt(cfg.SubCommandOpt('cmd2')) - self.useFixture(fixtures.MonkeyPatch('sys.stderr', moves.StringIO())) - self.assertRaises(SystemExit, self.conf, []) - self.assertTrue('multiple' in sys.stderr.getvalue()) - - -class SetDefaultsTestCase(BaseTestCase): - - def test_default_to_none(self): - opts = [cfg.StrOpt('foo', default='foo')] - self.conf.register_opts(opts) - cfg.set_defaults(opts, foo=None) - self.conf([]) - self.assertIsNone(self.conf.foo) - - def test_default_from_none(self): - opts = [cfg.StrOpt('foo')] - self.conf.register_opts(opts) - cfg.set_defaults(opts, foo='bar') - self.conf([]) - self.assertEqual('bar', self.conf.foo) - - def test_change_default(self): - opts = [cfg.StrOpt('foo', default='foo')] - self.conf.register_opts(opts) - cfg.set_defaults(opts, foo='bar') - self.conf([]) - self.assertEqual('bar', self.conf.foo) - - def test_change_default_many(self): - opts = [cfg.StrOpt('foo', default='foo'), - cfg.StrOpt('foo2', default='foo2')] - self.conf.register_opts(opts) - cfg.set_defaults(opts, foo='bar', foo2='bar2') - self.conf([]) - self.assertEqual('bar', self.conf.foo) - self.assertEqual('bar2', self.conf.foo2) - - def test_group_default_to_none(self): - opts = [cfg.StrOpt('foo', default='foo')] - self.conf.register_opts(opts, group='blaa') - cfg.set_defaults(opts, foo=None) - self.conf([]) - self.assertIsNone(self.conf.blaa.foo) - - def test_group_default_from_none(self): - opts = [cfg.StrOpt('foo')] - self.conf.register_opts(opts, group='blaa') - cfg.set_defaults(opts, foo='bar') - self.conf([]) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_group_change_default(self): - opts = [cfg.StrOpt('foo', default='foo')] - self.conf.register_opts(opts, group='blaa') - cfg.set_defaults(opts, foo='bar') - self.conf([]) - self.assertEqual('bar', self.conf.blaa.foo) - - -class DeprecatedOptionsTestCase(BaseTestCase): - - def test_deprecated_opts_equal(self): - d1 = cfg.DeprecatedOpt('oldfoo', group='oldgroup') - d2 = cfg.DeprecatedOpt('oldfoo', group='oldgroup') - self.assertEqual(d1, d2) - - def test_deprecated_opts_not_equal(self): - d1 = cfg.DeprecatedOpt('oldfoo', group='oldgroup') - d2 = cfg.DeprecatedOpt('oldfoo2', group='oldgroup') - self.assertNotEqual(d1, d2) - - -class MultipleDeprecatedOptionsTestCase(BaseTestCase): - - def test_conf_file_override_use_deprecated_name_and_group(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_opt(cfg.StrOpt('foo', - deprecated_name='oldfoo', - deprecated_group='oldgroup'), - group='blaa') - - paths = self.create_tempfiles([('test', - '[oldgroup]\n' - 'oldfoo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_conf_file_override_use_deprecated_opts(self): - self.conf.register_group(cfg.OptGroup('blaa')) - oldopts = [cfg.DeprecatedOpt('oldfoo', group='oldgroup')] - self.conf.register_opt(cfg.StrOpt('foo', deprecated_opts=oldopts), - group='blaa') - - paths = self.create_tempfiles([('test', - '[oldgroup]\n' - 'oldfoo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_conf_file_override_use_deprecated_multi_opts(self): - self.conf.register_group(cfg.OptGroup('blaa')) - oldopts = [cfg.DeprecatedOpt('oldfoo', group='oldgroup'), - cfg.DeprecatedOpt('oldfoo2', group='oldgroup2')] - self.conf.register_opt(cfg.StrOpt('foo', deprecated_opts=oldopts), - group='blaa') - - paths = self.create_tempfiles([('test', - '[oldgroup2]\n' - 'oldfoo2 = bar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bar', self.conf.blaa.foo) - - -class MultipleDeprecatedCliOptionsTestCase(BaseTestCase): - - def test_conf_file_override_use_deprecated_name_and_group(self): - self.conf.register_group(cfg.OptGroup('blaa')) - self.conf.register_cli_opt(cfg.StrOpt('foo', - deprecated_name='oldfoo', - deprecated_group='oldgroup'), - group='blaa') - - paths = self.create_tempfiles([('test', - '[oldgroup]\n' - 'oldfoo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_conf_file_override_use_deprecated_opts(self): - self.conf.register_group(cfg.OptGroup('blaa')) - oldopts = [cfg.DeprecatedOpt('oldfoo', group='oldgroup')] - self.conf.register_cli_opt(cfg.StrOpt('foo', deprecated_opts=oldopts), - group='blaa') - - paths = self.create_tempfiles([('test', - '[oldgroup]\n' - 'oldfoo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_conf_file_override_use_deprecated_multi_opts(self): - self.conf.register_group(cfg.OptGroup('blaa')) - oldopts = [cfg.DeprecatedOpt('oldfoo', group='oldgroup'), - cfg.DeprecatedOpt('oldfoo2', group='oldgroup2')] - self.conf.register_cli_opt(cfg.StrOpt('foo', deprecated_opts=oldopts), - group='blaa') - - paths = self.create_tempfiles([('test', - '[oldgroup2]\n' - 'oldfoo2 = bar\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bar', self.conf.blaa.foo) - - def test_conf_file_common_deprecated_group(self): - self.conf.register_group(cfg.OptGroup('foo')) - self.conf.register_group(cfg.OptGroup('bar')) - oldopts = [cfg.DeprecatedOpt('foo', group='DEFAULT')] - self.conf.register_opt(cfg.StrOpt('common_opt', - deprecated_opts=oldopts), - group='bar') - self.conf.register_opt(cfg.StrOpt('common_opt', - deprecated_opts=oldopts), - group='foo') - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bla\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bla', self.conf.foo.common_opt) - self.assertEqual('bla', self.conf.bar.common_opt) - - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' - 'foo = bla\n' - '[bar]\n' - 'common_opt = blabla\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bla', self.conf.foo.common_opt) - self.assertEqual('blabla', self.conf.bar.common_opt) - - paths = self.create_tempfiles([('test', - '[foo]\n' - 'common_opt = bla\n' - '[bar]\n' - 'common_opt = blabla\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('bla', self.conf.foo.common_opt) - self.assertEqual('blabla', self.conf.bar.common_opt) - - -class ChoicesTestCase(BaseTestCase): - - def test_choice_default(self): - self.conf.register_cli_opt(cfg.StrOpt('protocol', - default='http', - choices=['http', 'https', 'ftp'])) - self.conf([]) - self.assertEqual('http', self.conf.protocol) - - def test_choice_good(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', - choices=['bar1', 'bar2'])) - self.conf(['--foo', 'bar1']) - self.assertEqual('bar1', self.conf.foo) - - def test_choice_bad(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', - choices=['bar1', 'bar2'])) - self.assertRaises(SystemExit, self.conf, ['--foo', 'bar3']) - - def test_conf_file_choice_value(self): - self.conf.register_opt(cfg.StrOpt('foo', - choices=['bar1', 'bar2'])) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = bar1\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar1', self.conf.foo) - - def test_conf_file_choice_empty_value(self): - self.conf.register_opt(cfg.StrOpt('foo', - choices=['', 'bar1', 'bar2'])) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = \n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('', self.conf.foo) - - def test_conf_file_choice_none_value(self): - self.conf.register_opt(cfg.StrOpt('foo', - default=None, - choices=[None, 'bar1', 'bar2'])) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertIsNone(self.conf.foo) - - def test_conf_file_bad_choice_value(self): - self.conf.register_opt(cfg.StrOpt('foo', - choices=['bar1', 'bar2'])) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''foo = bar3\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'foo') - self.assertRaises(ValueError, getattr, self.conf, 'foo') - - def test_conf_file_choice_value_override(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', - choices=['baar', 'baaar'])) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--foo', 'baar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('baaar', self.conf.foo) - - def test_conf_file_choice_bad_default(self): - self.assertRaises(cfg.DefaultValueError, cfg.StrOpt, 'foo', - choices=['baar', 'baaar'], default='foobaz') - - -class PortChoicesTestCase(BaseTestCase): - - def test_choice_default(self): - self.conf.register_cli_opt(cfg.PortOpt('port', - default=455, - choices=[80, 455])) - self.conf([]) - self.assertEqual(455, self.conf.port) - - def test_choice_good_with_list(self): - self.conf.register_cli_opt(cfg.PortOpt('port', - choices=[80, 8080])) - self.conf(['--port', '80']) - self.assertEqual(80, self.conf.port) - - def test_choice_good_with_tuple(self): - self.conf.register_cli_opt(cfg.PortOpt('port', - choices=(80, 8080))) - self.conf(['--port', '80']) - self.assertEqual(80, self.conf.port) - - def test_choice_bad(self): - self.conf.register_cli_opt(cfg.PortOpt('port', - choices=[80, 8080])) - self.assertRaises(SystemExit, self.conf, ['--port', '8181']) - - def test_choice_out_range(self): - self.assertRaisesRegexp(ValueError, 'out of bounds', - cfg.PortOpt, 'port', choices=[80, 65537, 0]) - - def test_conf_file_choice_value(self): - self.conf.register_opt(cfg.PortOpt('port', - choices=[80, 8080])) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''port = 80\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'port')) - self.assertEqual(80, self.conf.port) - - def test_conf_file_bad_choice_value(self): - self.conf.register_opt(cfg.PortOpt('port', - choices=[80, 8080])) - - paths = self.create_tempfiles([('test', '[DEFAULT]\n''port = 8181\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertRaises(cfg.ConfigFileValueError, self.conf._get, 'port') - self.assertRaises(ValueError, getattr, self.conf, 'port') - - def test_conf_file_choice_value_override(self): - self.conf.register_cli_opt(cfg.PortOpt('port', - choices=[80, 8080])) - - paths = self.create_tempfiles([('1', - '[DEFAULT]\n' - 'port = 80\n'), - ('2', - '[DEFAULT]\n' - 'port = 8080\n')]) - - self.conf(['--port', '80', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'port')) - self.assertEqual(8080, self.conf.port) - - def test_conf_file_choice_bad_default(self): - self.assertRaises(cfg.DefaultValueError, cfg.PortOpt, 'port', - choices=[80, 8080], default=8181) - - -class RegexTestCase(BaseTestCase): - - def test_regex_good(self): - self.conf.register_cli_opt(cfg.StrOpt('foo', - regex='foo|bar')) - self.conf(['--foo', 'bar']) - self.assertEqual('bar', self.conf.foo) - self.conf(['--foo', 'foo']) - self.assertEqual('foo', self.conf.foo) - self.conf(['--foo', 'foobar']) - self.assertEqual('foobar', self.conf.foo) - - 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('bar', self.conf.foo) - - 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('foobar1', self.conf.foo) - self.conf(['--foo', "'foobar2'"]) - self.assertEqual('foobar2', self.conf.foo) - self.conf(['--foo', 'foobar3']) - self.assertEqual('foobar3', self.conf.foo) - self.conf(['--foo', 'foobar4"']) - self.assertEqual('foobar4"', self.conf.foo) - - 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('bar', self.conf.foo) - - 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('bAr1', self.conf.foo) - self.conf(['--foo', 'BaR2']) - self.assertEqual('BaR2', self.conf.foo) - self.conf(['--foo', 'baR3']) - self.assertEqual('baR3', self.conf.foo) - - 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('foo', self.conf.foo) - self.conf(['--foo', 'Bar']) - self.assertEqual('Bar', self.conf.foo) - self.conf(['--foo', 'FOObar']) - self.assertEqual('FOObar', self.conf.foo) - - 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('bAr2', self.conf.foo) - - 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('BaR', self.conf.foo) - - -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('12345', self.conf.foo) - - 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 URIOptMaxLengthTestCase(BaseTestCase): - - def test_uriopt_max_length_good(self): - self.conf.register_cli_opt(cfg.URIOpt('foo', max_length=30)) - self.conf(['--foo', 'http://www.example.com']) - self.assertEqual('http://www.example.com', self.conf.foo) - - def test_uriopt_max_length_bad(self): - self.conf.register_cli_opt(cfg.URIOpt('foo', max_length=30)) - self.assertRaises(SystemExit, self.conf, - ['--foo', 'http://www.example.com/versions']) - - -class PrintHelpTestCase(base.BaseTestCase): - - def test_print_help_without_init(self): - conf = cfg.ConfigOpts() - conf.register_opts([]) - self.assertRaises(cfg.NotInitializedError, - conf.print_help) - - def test_print_help_with_clear(self): - conf = cfg.ConfigOpts() - conf.register_opts([]) - conf([]) - conf.clear() - self.assertRaises(cfg.NotInitializedError, - conf.print_help) - - -class OptTestCase(base.BaseTestCase): - - def test_opt_eq(self): - d1 = cfg.ListOpt('oldfoo') - d2 = cfg.ListOpt('oldfoo') - self.assertEqual(d1, d2) - - def test_opt_not_eq(self): - d1 = cfg.ListOpt('oldfoo') - d2 = cfg.ListOpt('oldbar') - self.assertNotEqual(d1, d2) - - def test_illegal_name(self): - self.assertRaises(ValueError, cfg.BoolOpt, '_foo') - - -class SectionsTestCase(BaseTestCase): - def test_list_all_sections(self): - paths = self.create_tempfiles([('test.ini', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n'), - ('test2.ini', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n')]) - self.conf(args=[], default_config_files=paths) - self.assertEqual(['BLAA', 'DEFAULT'], - self.conf.list_all_sections()) - - def test_list_all_sections_post_mutate(self): - paths = self.create_tempfiles([('test.ini', - '[DEFAULT]\n' - 'foo = bar\n' - '[BLAA]\n' - 'bar = foo\n'), - ('test2.ini', - '[WOMBAT]\n' - 'woo = war\n' - '[BLAA]\n' - 'bar = foo\n')]) - self.conf(args=[], default_config_files=paths[:1]) - self.assertEqual(['BLAA', 'DEFAULT'], - self.conf.list_all_sections()) - - shutil.copy(paths[1], paths[0]) - self.conf.mutate_config_files() - self.assertEqual(['BLAA', 'DEFAULT', 'WOMBAT'], - self.conf.list_all_sections()) - - -class DeprecationWarningTestBase(BaseTestCase): - def setUp(self): - super(DeprecationWarningTestBase, self).setUp() - self.log_fixture = self.useFixture(fixtures.FakeLogger()) - self._parser_class = cfg.MultiConfigParser - - -class DeprecationWarningTestScenarios(DeprecationWarningTestBase): - scenarios = [('default-deprecated', dict(deprecated=True, - group='DEFAULT')), - ('default-not-deprecated', dict(deprecated=False, - group='DEFAULT')), - ('other-deprecated', dict(deprecated=True, - group='other')), - ('other-not-deprecated', dict(deprecated=False, - group='other')), - ] - - def test_deprecated_logging(self): - self.conf.register_opt(cfg.StrOpt('foo', deprecated_name='bar')) - self.conf.register_group(cfg.OptGroup('other')) - self.conf.register_opt(cfg.StrOpt('foo', deprecated_name='bar'), - group='other') - if self.deprecated: - content = 'bar=baz' - else: - content = 'foo=baz' - paths = self.create_tempfiles([('test', - '[' + self.group + ']\n' + - content + '\n')]) - - self.conf(['--config-file', paths[0]]) - # Reference these twice to verify they only get logged once - if self.group == 'DEFAULT': - self.assertEqual('baz', self.conf.foo) - self.assertEqual('baz', self.conf.foo) - else: - self.assertEqual('baz', self.conf.other.foo) - self.assertEqual('baz', self.conf.other.foo) - if self.deprecated: - expected = (self._parser_class._deprecated_opt_message % - {'dep_option': 'bar', - 'dep_group': self.group, - 'option': 'foo', - 'group': self.group} + '\n') - else: - expected = '' - self.assertEqual(expected, self.log_fixture.output) - - -class DeprecationWarningTests(DeprecationWarningTestBase): - def test_DeprecatedOpt(self): - default_deprecated = [cfg.DeprecatedOpt('bar')] - other_deprecated = [cfg.DeprecatedOpt('baz', group='other')] - self.conf.register_group(cfg.OptGroup('other')) - self.conf.register_opt(cfg.StrOpt('foo', - deprecated_opts=default_deprecated)) - self.conf.register_opt(cfg.StrOpt('foo', - deprecated_opts=other_deprecated), - group='other') - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' + - 'bar=baz\n' + - '[other]\n' + - 'baz=baz\n')]) - self.conf(['--config-file', paths[0]]) - self.assertEqual('baz', self.conf.foo) - self.assertEqual('baz', self.conf.other.foo) - self.assertIn('Option "bar" from group "DEFAULT"', - self.log_fixture.output) - self.assertIn('Option "baz" from group "other"', - self.log_fixture.output) - - def test_check_deprecated(self): - parser = self._parser_class() - deprecated_list = [('DEFAULT', 'bar')] - parser._check_deprecated(('DEFAULT', 'bar'), (None, 'foo'), - deprecated_list) - self.assert_message_logged('bar', 'DEFAULT', 'foo', 'DEFAULT') - - def assert_message_logged(self, deprecated_name, deprecated_group, - current_name, current_group): - expected = (self._parser_class._deprecated_opt_message % - {'dep_option': deprecated_name, - 'dep_group': deprecated_group, - 'option': current_name, - 'group': current_group} - ) - self.assertEqual(expected + '\n', self.log_fixture.output) - - def test_deprecated_for_removal(self): - self.conf.register_opt(cfg.StrOpt('foo', - deprecated_for_removal=True)) - self.conf.register_opt(cfg.StrOpt('bar', - deprecated_for_removal=True)) - paths = self.create_tempfiles([('test', - '[DEFAULT]\n' + - 'foo=bar\n')]) - self.conf(['--config-file', paths[0]]) - # Multiple references should be logged only once. - self.assertEqual('bar', self.conf.foo) - self.assertEqual('bar', self.conf.foo) - # Options not set in the config should not be logged. - self.assertIsNone(self.conf.bar) - expected = ('Option "foo" from group "DEFAULT" is deprecated for ' - 'removal. Its value may be silently ignored in the ' - 'future.\n') - self.assertEqual(expected, self.log_fixture.output) - - def test_deprecated_for_removal_with_group(self): - self.conf.register_group(cfg.OptGroup('other')) - self.conf.register_opt(cfg.StrOpt('foo', - deprecated_for_removal=True), - group='other') - self.conf.register_opt(cfg.StrOpt('bar', - deprecated_for_removal=True), - group='other') - paths = self.create_tempfiles([('test', - '[other]\n' + - 'foo=bar\n')]) - self.conf(['--config-file', paths[0]]) - # Multiple references should be logged only once. - self.assertEqual('bar', self.conf.other.foo) - self.assertEqual('bar', self.conf.other.foo) - # Options not set in the config should not be logged. - self.assertIsNone(self.conf.other.bar) - expected = ('Option "foo" from group "other" is deprecated for ' - 'removal. Its value may be silently ignored in the ' - 'future.\n') - self.assertEqual(expected, self.log_fixture.output) - - def test_deprecated_with_dest(self): - self.conf.register_group(cfg.OptGroup('other')) - self.conf.register_opt(cfg.StrOpt('foo-bar', deprecated_name='bar', - dest='foo'), - group='other') - content = 'bar=baz' - paths = self.create_tempfiles([('test', - '[other]\n' + - content + '\n')]) - - self.conf(['--config-file', paths[0]]) - self.assertEqual('baz', self.conf.other.foo) - expected = (self._parser_class._deprecated_opt_message % - {'dep_option': 'bar', - 'dep_group': 'other', - 'option': 'foo-bar', - 'group': 'other'} + '\n') - self.assertEqual(expected, self.log_fixture.output) diff --git a/oslo_config/tests/test_cfgfilter.py b/oslo_config/tests/test_cfgfilter.py deleted file mode 100644 index aa0d2db..0000000 --- a/oslo_config/tests/test_cfgfilter.py +++ /dev/null @@ -1,362 +0,0 @@ -# Copyright 2014 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslotest import base as test_base -import testtools - -from oslo_config import cfg -from oslo_config import cfgfilter - - -class BaseTestCase(test_base.BaseTestCase): - - def setUp(self, conf=None): - super(BaseTestCase, self).setUp() - if conf is None: - self.conf = cfg.ConfigOpts() - else: - self.conf = conf - self.fconf = cfgfilter.ConfigFilter(self.conf) - - -class RegisterTestCase(BaseTestCase): - - def test_register_opt_default(self): - self.fconf.register_opt(cfg.StrOpt('foo', default='bar')) - - self.assertEqual('bar', self.fconf.foo) - self.assertEqual('bar', self.fconf['foo']) - self.assertIn('foo', self.fconf) - self.assertEqual(['foo'], list(self.fconf)) - self.assertEqual(1, len(self.fconf)) - - self.assertNotIn('foo', self.conf) - self.assertEqual(0, len(self.conf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf, 'foo') - - def test_register_opt_none_default(self): - self.fconf.register_opt(cfg.StrOpt('foo')) - - self.assertIsNone(self.fconf.foo) - self.assertIsNone(self.fconf['foo']) - self.assertIn('foo', self.fconf) - self.assertEqual(['foo'], list(self.fconf)) - self.assertEqual(1, len(self.fconf)) - - self.assertNotIn('foo', self.conf) - self.assertEqual(0, len(self.conf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf, 'foo') - - def test_register_grouped_opt_default(self): - self.fconf.register_opt(cfg.StrOpt('foo', default='bar'), - group='blaa') - - self.assertEqual('bar', self.fconf.blaa.foo) - self.assertEqual('bar', self.fconf['blaa']['foo']) - self.assertIn('blaa', self.fconf) - self.assertIn('foo', self.fconf.blaa) - self.assertEqual(['blaa'], list(self.fconf)) - self.assertEqual(['foo'], list(self.fconf.blaa)) - self.assertEqual(1, len(self.fconf)) - self.assertEqual(1, len(self.fconf.blaa)) - - self.assertNotIn('blaa', self.conf) - self.assertEqual(0, len(self.conf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf, 'blaa') - - def test_register_grouped_opt_none_default(self): - self.fconf.register_opt(cfg.StrOpt('foo'), group='blaa') - - self.assertIsNone(self.fconf.blaa.foo) - self.assertIsNone(self.fconf['blaa']['foo']) - self.assertIn('blaa', self.fconf) - self.assertIn('foo', self.fconf.blaa) - self.assertEqual(['blaa'], list(self.fconf)) - self.assertEqual(['foo'], list(self.fconf.blaa)) - self.assertEqual(1, len(self.fconf)) - self.assertEqual(1, len(self.fconf.blaa)) - - self.assertNotIn('blaa', self.conf) - self.assertEqual(0, len(self.conf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf, 'blaa') - - def test_register_group(self): - group = cfg.OptGroup('blaa') - self.fconf.register_group(group) - self.fconf.register_opt(cfg.StrOpt('foo'), group=group) - - self.assertIsNone(self.fconf.blaa.foo) - self.assertIsNone(self.fconf['blaa']['foo']) - self.assertIn('blaa', self.fconf) - self.assertIn('foo', self.fconf.blaa) - self.assertEqual(['blaa'], list(self.fconf)) - self.assertEqual(['foo'], list(self.fconf.blaa)) - self.assertEqual(1, len(self.fconf)) - self.assertEqual(1, len(self.fconf.blaa)) - - self.assertNotIn('blaa', self.conf) - self.assertEqual(0, len(self.conf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.conf, 'blaa') - - def test_register_opts(self): - self.fconf.register_opts([cfg.StrOpt('foo'), - cfg.StrOpt('bar')]) - self.assertIn('foo', self.fconf) - self.assertIn('bar', self.fconf) - self.assertNotIn('foo', self.conf) - self.assertNotIn('bar', self.conf) - - def test_register_known_cli_opt(self): - self.conf.register_opt(cfg.StrOpt('foo')) - self.fconf.register_cli_opt(cfg.StrOpt('foo')) - self.assertIn('foo', self.fconf) - self.assertIn('foo', self.conf) - - def test_register_unknown_cli_opt(self): - with testtools.ExpectedException(cfgfilter.CliOptRegisteredError): - self.fconf.register_cli_opt(cfg.StrOpt('foo')) - - def test_register_known_cli_opts(self): - self.conf.register_cli_opts([cfg.StrOpt('foo'), cfg.StrOpt('bar')]) - self.fconf.register_cli_opts([cfg.StrOpt('foo'), cfg.StrOpt('bar')]) - self.assertIn('foo', self.fconf) - self.assertIn('bar', self.fconf) - self.assertIn('foo', self.conf) - self.assertIn('bar', self.conf) - - def test_register_unknown_cli_opts(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - with testtools.ExpectedException(cfgfilter.CliOptRegisteredError): - self.fconf.register_cli_opts([ - cfg.StrOpt('foo'), - cfg.StrOpt('bar') - ]) - - def test_register_opts_grouped(self): - self.fconf.register_opts([cfg.StrOpt('foo'), cfg.StrOpt('bar')], - group='blaa') - self.assertIn('foo', self.fconf.blaa) - self.assertIn('bar', self.fconf.blaa) - self.assertNotIn('blaa', self.conf) - - def test_register_known_cli_opt_grouped(self): - self.conf.register_cli_opt(cfg.StrOpt('foo'), group='blaa') - self.fconf.register_cli_opt(cfg.StrOpt('foo'), group='blaa') - self.assertIn('foo', self.fconf.blaa) - self.assertIn('blaa', self.fconf) - self.assertIn('blaa', self.conf) - - def test_register_unknown_cli_opt_grouped(self): - with testtools.ExpectedException(cfgfilter.CliOptRegisteredError): - self.fconf.register_cli_opt(cfg.StrOpt('foo'), group='blaa') - - def test_register_known_cli_opts_grouped(self): - self.conf.register_cli_opts([cfg.StrOpt('foo'), cfg.StrOpt('bar')], - group='blaa') - self.fconf.register_cli_opts([cfg.StrOpt('foo'), cfg.StrOpt('bar')], - group='blaa') - self.assertIn('foo', self.fconf.blaa) - self.assertIn('bar', self.fconf.blaa) - self.assertIn('blaa', self.fconf) - self.assertIn('blaa', self.conf) - - def test_register_unknown_opts_grouped(self): - self.conf.register_cli_opts([cfg.StrOpt('bar')], group='blaa') - with testtools.ExpectedException(cfgfilter.CliOptRegisteredError): - self.fconf.register_cli_opts([ - cfg.StrOpt('foo'), - cfg.StrOpt('bar') - ], group='blaa') - - def test_unknown_opt(self): - self.assertNotIn('foo', self.fconf) - self.assertEqual(0, len(self.fconf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.fconf, 'foo') - self.assertNotIn('blaa', self.conf) - - def test_blocked_opt(self): - self.conf.register_opt(cfg.StrOpt('foo')) - - self.assertIn('foo', self.conf) - self.assertEqual(1, len(self.conf)) - self.assertIsNone(self.conf.foo) - self.assertNotIn('foo', self.fconf) - self.assertEqual(0, len(self.fconf)) - self.assertRaises(cfg.NoSuchOptError, getattr, self.fconf, 'foo') - - def test_already_registered_opt(self): - self.conf.register_opt(cfg.StrOpt('foo')) - self.fconf.register_opt(cfg.StrOpt('foo')) - - self.assertIn('foo', self.conf) - self.assertEqual(1, len(self.conf)) - self.assertIsNone(self.conf.foo) - self.assertIn('foo', self.fconf) - self.assertEqual(1, len(self.fconf)) - self.assertIsNone(self.fconf.foo) - - self.conf.set_override('foo', 'bar') - - self.assertEqual('bar', self.conf.foo) - self.assertEqual('bar', self.fconf.foo) - - def test_already_registered_opts(self): - self.conf.register_opts([cfg.StrOpt('foo'), - cfg.StrOpt('fu')]) - self.fconf.register_opts([cfg.StrOpt('foo'), - cfg.StrOpt('bu')]) - - self.assertIn('foo', self.conf) - self.assertIn('fu', self.conf) - self.assertNotIn('bu', self.conf) - self.assertEqual(2, len(self.conf)) - self.assertIsNone(self.conf.foo) - self.assertIsNone(self.conf.fu) - self.assertIn('foo', self.fconf) - self.assertIn('bu', self.fconf) - self.assertNotIn('fu', self.fconf) - self.assertEqual(2, len(self.fconf)) - self.assertIsNone(self.fconf.foo) - self.assertIsNone(self.fconf.bu) - - self.conf.set_override('foo', 'bar') - - self.assertEqual('bar', self.conf.foo) - self.assertEqual('bar', self.fconf.foo) - - def test_already_registered_cli_opt(self): - self.conf.register_cli_opt(cfg.StrOpt('foo')) - self.fconf.register_cli_opt(cfg.StrOpt('foo')) - - self.assertIn('foo', self.conf) - self.assertEqual(1, len(self.conf)) - self.assertIsNone(self.conf.foo) - self.assertIn('foo', self.fconf) - self.assertEqual(1, len(self.fconf)) - self.assertIsNone(self.fconf.foo) - - self.conf.set_override('foo', 'bar') - - self.assertEqual('bar', self.conf.foo) - self.assertEqual('bar', self.fconf.foo) - - def test_already_registered_cli_opts(self): - self.conf.register_cli_opts([cfg.StrOpt('foo'), - cfg.StrOpt('fu')]) - self.fconf.register_cli_opts([cfg.StrOpt('foo'), - cfg.StrOpt('fu')]) - - self.assertIn('foo', self.conf) - self.assertIn('fu', self.conf) - self.assertEqual(2, len(self.conf)) - self.assertIsNone(self.conf.foo) - self.assertIsNone(self.conf.fu) - self.assertIn('foo', self.fconf) - self.assertIn('fu', self.fconf) - self.assertEqual(2, len(self.fconf)) - self.assertIsNone(self.fconf.foo) - self.assertIsNone(self.fconf.fu) - - self.conf.set_override('foo', 'bar') - - self.assertEqual('bar', self.conf.foo) - self.assertEqual('bar', self.fconf.foo) - - -class ImportTestCase(BaseTestCase): - - def setUp(self): - super(ImportTestCase, self).setUp(cfg.CONF) - - def test_import_opt(self): - self.assertFalse(hasattr(self.conf, 'fblaa')) - self.conf.import_opt('fblaa', 'oslo_config.tests.testmods.fblaa_opt') - self.assertTrue(hasattr(self.conf, 'fblaa')) - self.assertFalse(hasattr(self.fconf, 'fblaa')) - self.fconf.import_opt('fblaa', 'oslo_config.tests.testmods.fblaa_opt') - self.assertTrue(hasattr(self.fconf, 'fblaa')) - - def test_import_opt_in_group(self): - self.assertFalse(hasattr(self.conf, 'fbar')) - self.conf.import_opt('foo', 'oslo_config.tests.testmods.fbar_foo_opt', - group='fbar') - self.assertTrue(hasattr(self.conf, 'fbar')) - self.assertTrue(hasattr(self.conf.fbar, 'foo')) - self.assertFalse(hasattr(self.fconf, 'fbar')) - self.fconf.import_opt('foo', 'oslo_config.tests.testmods.fbar_foo_opt', - group='fbar') - self.assertTrue(hasattr(self.fconf, 'fbar')) - self.assertTrue(hasattr(self.fconf.fbar, 'foo')) - - def test_import_group(self): - self.assertFalse(hasattr(self.conf, 'fbaar')) - self.conf.import_group('fbaar', - 'oslo_config.tests.testmods.fbaar_baa_opt') - self.assertTrue(hasattr(self.conf, 'fbaar')) - self.assertTrue(hasattr(self.conf.fbaar, 'baa')) - self.assertFalse(hasattr(self.fconf, 'fbaar')) - self.fconf.import_group('fbaar', - 'oslo_config.tests.testmods.fbaar_baa_opt') - self.assertTrue(hasattr(self.fconf, 'fbaar')) - self.assertTrue(hasattr(self.fconf.fbaar, 'baa')) - - -class ExposeTestCase(BaseTestCase): - - def test_expose_opt(self): - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertFalse(hasattr(self.fconf, 'foo')) - - self.conf.register_opt(cfg.StrOpt('foo')) - self.conf.set_override('foo', 'bar') - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEqual('bar', self.conf.foo) - self.assertFalse(hasattr(self.fconf, 'foo')) - - self.fconf.expose_opt('foo') - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertTrue(hasattr(self.fconf, 'foo')) - self.assertEqual('bar', self.fconf.foo) - - def test_expose_opt_with_group(self): - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertFalse(hasattr(self.fconf, 'foo')) - - self.conf.register_opt(cfg.StrOpt('foo'), group='group') - self.conf.set_override('foo', 'bar', group='group') - - self.assertTrue(hasattr(self.conf.group, 'foo')) - self.assertEqual('bar', self.conf.group.foo) - self.assertFalse(hasattr(self.fconf, 'group')) - - self.fconf.expose_opt('foo', group='group') - self.assertTrue(hasattr(self.conf.group, 'foo')) - self.assertTrue(hasattr(self.fconf.group, 'foo')) - self.assertEqual('bar', self.fconf.group.foo) - - def test_expose_group(self): - self.conf.register_opts([cfg.StrOpt('foo'), - cfg.StrOpt('bar')], group='group') - self.conf.register_opts([cfg.StrOpt('foo'), - cfg.StrOpt('bar')], group='another') - self.conf.set_override('foo', 'a', group='group') - self.conf.set_override('bar', 'b', group='group') - - self.fconf.expose_group('group') - - self.assertEqual('a', self.fconf.group.foo) - self.assertEqual('b', self.fconf.group.bar) - self.assertFalse(hasattr(self.fconf, 'another')) - self.assertTrue(hasattr(self.conf, 'another')) diff --git a/oslo_config/tests/test_fixture.conf b/oslo_config/tests/test_fixture.conf deleted file mode 100644 index 8049132..0000000 --- a/oslo_config/tests/test_fixture.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -first_test_opt=loaded_value_1 -second_test_opt=loaded_value_2 \ No newline at end of file diff --git a/oslo_config/tests/test_fixture.py b/oslo_config/tests/test_fixture.py deleted file mode 100644 index 6e2062d..0000000 --- a/oslo_config/tests/test_fixture.py +++ /dev/null @@ -1,200 +0,0 @@ -# -# Copyright 2013 Mirantis, Inc. -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslotest import base - -from oslo_config import cfg -from oslo_config import fixture as config - - -class ConfigTestCase(base.BaseTestCase): - - def _make_fixture(self): - conf = cfg.ConfigOpts() - config_fixture = config.Config(conf) - config_fixture.setUp() - config_fixture.register_opt(cfg.StrOpt( - 'testing_option', default='initial_value')) - config_fixture.register_opt(cfg.IntOpt( - 'test2', min=0, default=5)) - config_fixture.register_opt(cfg.StrOpt( - 'test3', choices=['a', 'b'], default='a')) - return config_fixture - - def test_overridden_value(self): - f = self._make_fixture() - self.assertEqual(f.conf.get('testing_option'), 'initial_value') - f.config(testing_option='changed_value') - self.assertEqual('changed_value', - f.conf.get('testing_option')) - - def test_overridden_value_with_enforce_type(self): - f = self._make_fixture() - self.assertEqual(5, f.conf.get('test2')) - self.assertEqual('a', f.conf.get('test3')) - # with enforce_type=False - f.config(test2=-1) - self.assertEqual(-1, f.conf.get('test2')) - f.config(test3='c') - self.assertEqual('c', f.conf.get('test3')) - # with enforce_type=True - self.assertRaises(ValueError, f.config, test2=-1, enforce_type=True) - self.assertRaises(ValueError, f.config, test3='c', enforce_type=True) - - def test_cleanup(self): - f = self._make_fixture() - f.config(testing_option='changed_value') - self.assertEqual(f.conf.get('testing_option'), - 'changed_value') - f.conf.reset() - self.assertEqual(f.conf.get('testing_option'), 'initial_value') - - def test_register_option(self): - f = self._make_fixture() - opt = cfg.StrOpt('new_test_opt', default='initial_value') - f.register_opt(opt) - self.assertEqual(f.conf.get('new_test_opt'), - opt.default) - - def test_register_options(self): - f = self._make_fixture() - opt1 = cfg.StrOpt('first_test_opt', default='initial_value_1') - opt2 = cfg.StrOpt('second_test_opt', default='initial_value_2') - f.register_opts([opt1, opt2]) - self.assertEqual(f.conf.get('first_test_opt'), opt1.default) - self.assertEqual(f.conf.get('second_test_opt'), opt2.default) - - def test_cleanup_unregister_option(self): - f = self._make_fixture() - opt = cfg.StrOpt('new_test_opt', default='initial_value') - f.register_opt(opt) - self.assertEqual(f.conf.get('new_test_opt'), - opt.default) - f.cleanUp() - self.assertRaises(cfg.NoSuchOptError, f.conf.get, 'new_test_opt') - - def test_register_cli_option(self): - f = self._make_fixture() - opt = cfg.StrOpt('new_test_opt', default='initial_value') - f.register_cli_opt(opt) - self.assertEqual(f.conf.get('new_test_opt'), - opt.default) - - def test_register_cli_options(self): - f = self._make_fixture() - opt1 = cfg.StrOpt('first_test_opt', default='initial_value_1') - opt2 = cfg.StrOpt('second_test_opt', default='initial_value_2') - f.register_cli_opts([opt1, opt2]) - self.assertEqual(f.conf.get('first_test_opt'), opt1.default) - self.assertEqual(f.conf.get('second_test_opt'), opt2.default) - - def test_cleanup_unregister_cli_option(self): - f = self._make_fixture() - opt = cfg.StrOpt('new_test_opt', default='initial_value') - f.register_cli_opt(opt) - self.assertEqual(f.conf.get('new_test_opt'), - opt.default) - f.cleanUp() - self.assertRaises(cfg.NoSuchOptError, f.conf.get, 'new_test_opt') - - def test_load_raw_values(self): - f = self._make_fixture() - f.load_raw_values(first_test_opt='loaded_value_1', - second_test_opt='loaded_value_2') - - # Must not be registered. - self.assertRaises(cfg.NoSuchOptError, f.conf.get, 'first_test_opt') - self.assertRaises(cfg.NoSuchOptError, f.conf.get, 'second_test_opt') - - opt1 = cfg.StrOpt('first_test_opt', default='initial_value_1') - opt2 = cfg.StrOpt('second_test_opt', default='initial_value_2') - - f.register_opt(opt1) - f.register_opt(opt2) - - self.assertEqual(f.conf.first_test_opt, 'loaded_value_1') - self.assertEqual(f.conf.second_test_opt, 'loaded_value_2') - - # Cleanup. - f.cleanUp() - - # Must no longer be registered. - self.assertRaises(cfg.NoSuchOptError, f.conf.get, 'first_test_opt') - self.assertRaises(cfg.NoSuchOptError, f.conf.get, 'second_test_opt') - - # Even when registered, must be default. - f.register_opt(opt1) - f.register_opt(opt2) - self.assertEqual(f.conf.first_test_opt, 'initial_value_1') - self.assertEqual(f.conf.second_test_opt, 'initial_value_2') - - def test_assert_default_files_cleanup(self): - """Assert that using the fixture forces a clean list.""" - f = self._make_fixture() - self.assertNotIn('default_config_files', f.conf) - - config_files = ['./test_fixture.conf'] - f.set_config_files(config_files) - - self.assertEqual(f.conf.default_config_files, config_files) - f.cleanUp() - - self.assertNotIn('default_config_files', f.conf) - - def test_load_custom_files(self): - f = self._make_fixture() - self.assertNotIn('default_config_files', f.conf) - config_files = ['./oslo_config/tests/test_fixture.conf'] - f.set_config_files(config_files) - - opt1 = cfg.StrOpt('first_test_opt', default='initial_value_1') - opt2 = cfg.StrOpt('second_test_opt', default='initial_value_2') - - f.register_opt(opt1) - f.register_opt(opt2) - - self.assertEqual('loaded_value_1', f.conf.get('first_test_opt')) - self.assertEqual('loaded_value_2', f.conf.get('second_test_opt')) - - def test_set_default(self): - f = self._make_fixture() - opt = cfg.StrOpt('new_test_opt', default='initial_value') - # Register the option directly so it is not cleaned up by the - # fixture. - f.conf.register_opt(opt) - f.set_default( - name='new_test_opt', - default='alternate_value', - ) - self.assertEqual('alternate_value', f.conf.new_test_opt) - f.cleanUp() - self.assertEqual('initial_value', f.conf.new_test_opt) - - def test_set_default_group(self): - f = self._make_fixture() - opt = cfg.StrOpt('new_test_opt', default='initial_value') - # Register the option directly so it is not cleaned up by the - # fixture. - f.conf.register_opt(opt, group='foo') - f.set_default( - name='new_test_opt', - default='alternate_value', - group='foo', - ) - self.assertEqual('alternate_value', f.conf.foo.new_test_opt) - f.cleanUp() - self.assertEqual('initial_value', f.conf.foo.new_test_opt) diff --git a/oslo_config/tests/test_generator.py b/oslo_config/tests/test_generator.py deleted file mode 100644 index 1806dd1..0000000 --- a/oslo_config/tests/test_generator.py +++ /dev/null @@ -1,1268 +0,0 @@ -# Copyright 2014 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import sys - -import fixtures -import mock -from oslotest import base -from six import moves -import tempfile -import testscenarios - -from oslo_config import cfg -from oslo_config import fixture as config_fixture -from oslo_config import generator -from oslo_config import types - -load_tests = testscenarios.load_tests_apply_scenarios - - -def custom_type(a): - """Something that acts like a type, but isn't known""" - return a - - -class GeneratorTestCase(base.BaseTestCase): - - groups = { - 'group1': cfg.OptGroup(name='group1', - help='Lorem ipsum dolor sit amet, consectetur ' - 'adipisicing elit, sed do eiusmod tempor ' - 'incididunt ut labore et dolore magna ' - 'aliqua. Ut enim ad minim veniam, quis ' - 'nostrud exercitation ullamco laboris ' - 'nisi ut aliquip ex ea commodo ' - 'consequat. Duis aute irure dolor in.'), - 'group2': cfg.OptGroup(name='group2', title='Group 2'), - 'foo': cfg.OptGroup(name='foo', title='Foo Title', help='foo help'), - } - - opts = { - 'foo': cfg.StrOpt('foo', help='foo option'), - 'bar': cfg.StrOpt('bar', help='bar option'), - 'foo-bar': cfg.StrOpt('foo-bar', help='foobar'), - 'no_help': cfg.StrOpt('no_help'), - 'long_help': cfg.StrOpt('long_help', - help='Lorem ipsum dolor sit amet, consectetur ' - 'adipisicing elit, sed do eiusmod tempor ' - 'incididunt ut labore et dolore magna ' - 'aliqua. Ut enim ad minim veniam, quis ' - 'nostrud exercitation ullamco laboris ' - 'nisi ut aliquip ex ea commodo ' - 'consequat. Duis aute irure dolor in ' - 'reprehenderit in voluptate velit esse ' - 'cillum dolore eu fugiat nulla ' - 'pariatur. Excepteur sint occaecat ' - 'cupidatat non proident, sunt in culpa ' - 'qui officia deserunt mollit anim id est ' - 'laborum.'), - 'long_help_pre': cfg.StrOpt('long_help_pre', - help='This is a very long help text which ' - 'is preformatted with line breaks. ' - 'It should break when it is too long ' - 'but also keep the specified line ' - 'breaks. This makes it possible to ' - 'create lists with items:\n' - '\n' - '* item 1\n' - '* item 2\n' - '\n' - 'and should increase the ' - 'readability.'), - 'choices_opt': cfg.StrOpt('choices_opt', - default='a', - choices=(None, '', 'a', 'b', 'c'), - help='a string with choices'), - 'deprecated_opt_without_deprecated_group': cfg.StrOpt( - 'bar', deprecated_name='foobar', help='deprecated'), - 'deprecated_for_removal_opt': cfg.StrOpt( - 'bar', deprecated_for_removal=True, help='deprecated for removal'), - 'deprecated_reason_opt': cfg.BoolOpt( - 'turn_off_stove', - default=False, - deprecated_for_removal=True, - deprecated_reason='This was supposed to work but it really, ' - 'really did not. Always buy house insurance.', - help='DEPRECATED: Turn off stove'), - 'deprecated_opt_with_deprecated_since': cfg.BoolOpt( - 'tune_in', - deprecated_for_removal=True, - deprecated_since='13.0'), - 'deprecated_opt_with_deprecated_group': cfg.StrOpt( - 'bar', deprecated_name='foobar', deprecated_group='group1', - help='deprecated'), - # Unknown Opt default must be a string - 'unknown_type': cfg.Opt('unknown_opt', - default='123', - help='unknown', - type=types.String(type_name='unknown type')), - 'str_opt': cfg.StrOpt('str_opt', - default='foo bar', - help='a string'), - 'str_opt_sample_default': cfg.StrOpt('str_opt', - default='fooishbar', - help='a string'), - 'str_opt_with_space': cfg.StrOpt('str_opt', - default=' foo bar ', - help='a string with spaces'), - 'bool_opt': cfg.BoolOpt('bool_opt', - default=False, - help='a boolean'), - 'int_opt': cfg.IntOpt('int_opt', - default=10, - min=1, - max=20, - help='an integer'), - 'int_opt_min_0': cfg.IntOpt('int_opt_min_0', - default=10, - min=0, - max=20, - help='an integer'), - 'int_opt_max_0': cfg.IntOpt('int_opt_max_0', - default=-1, - max=0, - help='an integer'), - 'float_opt': cfg.FloatOpt('float_opt', - default=0.1, - help='a float'), - 'list_opt': cfg.ListOpt('list_opt', - default=['1', '2', '3'], - help='a list'), - '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'), - 'hostname_opt': cfg.HostnameOpt('hostname_opt', - default='compute01.nova.site1', - help='a hostname'), - 'uri_opt': cfg.URIOpt('uri_opt', - default='http://example.com', - help='a URI'), - 'multi_opt': cfg.MultiStrOpt('multi_opt', - default=['1', '2', '3'], - help='multiple strings'), - 'multi_opt_none': cfg.MultiStrOpt('multi_opt_none', - help='multiple strings'), - 'multi_opt_empty': cfg.MultiStrOpt('multi_opt_empty', - default=[], - help='multiple strings'), - 'multi_opt_sample_default': cfg.MultiStrOpt('multi_opt', - default=['1', '2', '3'], - sample_default=['5', '6'], - help='multiple strings'), - 'string_type_with_bad_default': cfg.Opt('string_type_with_bad_default', - help='string with bad default', - default=4096), - 'native_str_type': cfg.Opt('native_str_type', - help='native help', - type=str), - 'native_int_type': cfg.Opt('native_int_type', - help='native help', - type=int), - 'native_float_type': cfg.Opt('native_float_type', - help='native help', - type=float), - 'custom_type': cfg.Opt('custom_type', - help='custom help', - type=custom_type), - 'custom_type_name': cfg.Opt('custom_opt_type', - type=types.Integer(type_name='port' - ' number'), - default=5511, - help='this is a port'), - } - - content_scenarios = [ - ('empty', - dict(opts=[], expected='''[DEFAULT] -''')), - ('single_namespace', - dict(opts=[('test', [(None, [opts['foo']])])], - expected='''[DEFAULT] - -# -# From test -# - -# foo option (string value) -#foo = -''')), - ('multiple_namespaces', - dict(opts=[('test', [(None, [opts['foo']])]), - ('other', [(None, [opts['bar']])])], - expected='''[DEFAULT] - -# -# From other -# - -# bar option (string value) -#bar = - -# -# From test -# - -# foo option (string value) -#foo = -''')), - ('group', - dict(opts=[('test', [(groups['group1'], [opts['foo']])])], - expected='''[DEFAULT] - - -[group1] -# Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do -# eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim -# ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut -# aliquip ex ea commodo consequat. Duis aute irure dolor in. - -# -# From test -# - -# foo option (string value) -#foo = -''')), - ('empty_group', - dict(opts=[('test', [(groups['group1'], [])])], - expected='''[DEFAULT] -''')), - ('multiple_groups', - dict(opts=[('test', [(groups['group1'], [opts['foo']]), - (groups['group2'], [opts['bar']])])], - expected='''[DEFAULT] - - -[group1] -# Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do -# eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim -# ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut -# aliquip ex ea commodo consequat. Duis aute irure dolor in. - -# -# From test -# - -# foo option (string value) -#foo = - - -[group2] - -# -# From test -# - -# bar option (string value) -#bar = -''')), - ('group_in_multiple_namespaces', - dict(opts=[('test', [(groups['group1'], [opts['foo']])]), - ('other', [(groups['group1'], [opts['bar']])])], - expected='''[DEFAULT] - - -[group1] -# Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do -# eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim -# ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut -# aliquip ex ea commodo consequat. Duis aute irure dolor in. - -# -# From other -# - -# bar option (string value) -#bar = - -# -# From test -# - -# foo option (string value) -#foo = -''')), - ('hyphenated_name', - dict(opts=[('test', [(None, [opts['foo-bar']])])], - expected='''[DEFAULT] - -# -# From test -# - -# foobar (string value) -#foo_bar = -''')), - ('no_help', - dict(opts=[('test', [(None, [opts['no_help']])])], - log_warning=('"%s" is missing a help string', 'no_help'), - expected='''[DEFAULT] - -# -# From test -# - -# (string value) -#no_help = -''')), - ('long_help', - dict(opts=[('test', [(None, [opts['long_help']])])], - expected='''[DEFAULT] - -# -# From test -# - -# Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do -# eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim -# ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut -# aliquip ex ea commodo consequat. Duis aute irure dolor in -# reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla -# pariatur. Excepteur sint occaecat cupidatat non proident, sunt in -# culpa qui officia deserunt mollit anim id est laborum. (string -# value) -#long_help = -''')), - ('long_help_wrap_at_40', - dict(opts=[('test', [(None, [opts['long_help']])])], - wrap_width=40, - expected='''[DEFAULT] - -# -# From test -# - -# Lorem ipsum dolor sit amet, -# consectetur adipisicing elit, sed do -# eiusmod tempor incididunt ut labore et -# dolore magna aliqua. Ut enim ad minim -# veniam, quis nostrud exercitation -# ullamco laboris nisi ut aliquip ex ea -# commodo consequat. Duis aute irure -# dolor in reprehenderit in voluptate -# velit esse cillum dolore eu fugiat -# nulla pariatur. Excepteur sint -# occaecat cupidatat non proident, sunt -# in culpa qui officia deserunt mollit -# anim id est laborum. (string value) -#long_help = -''')), - ('long_help_no_wrapping', - dict(opts=[('test', [(None, [opts['long_help']])])], - wrap_width=0, - expected='''[DEFAULT] - -# -# From test -# - -''' # noqa -'# Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod ' -'tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, ' -'quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo ' -'consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse ' -'cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat ' -'non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. ' -'(string value)' -''' -#long_help = -''')), - ('long_help_with_preformatting', - dict(opts=[('test', [(None, [opts['long_help_pre']])])], - wrap_width=70, - expected='''[DEFAULT] - -# -# From test -# - -# This is a very long help text which is preformatted with line -# breaks. It should break when it is too long but also keep the -# specified line breaks. This makes it possible to create lists with -# items: -# -# * item 1 -# * item 2 -# -# and should increase the readability. (string value) -#long_help_pre = -''')), - ('choices_opt', - dict(opts=[('test', [(None, [opts['choices_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a string with choices (string value) -# Allowed values: , '', a, b, c -#choices_opt = a -''')), - ('deprecated opt without deprecated group', - dict(opts=[('test', - [(groups['foo'], - [opts['deprecated_opt_without_deprecated_group']])])], - expected='''[DEFAULT] - - -[foo] -# foo help - -# -# From test -# - -# deprecated (string value) -# Deprecated group/name - [foo]/foobar -#bar = -''')), - ('deprecated_for_removal', - dict(opts=[('test', [(groups['foo'], - [opts['deprecated_for_removal_opt']])])], - expected='''[DEFAULT] - - -[foo] -# foo help - -# -# From test -# - -# DEPRECATED: deprecated for removal (string value) -# This option is deprecated for removal. -# Its value may be silently ignored in the future. -#bar = -''')), - ('deprecated_reason', - dict(opts=[('test', [(groups['foo'], - [opts['deprecated_reason_opt']])])], - expected='''[DEFAULT] - - -[foo] -# foo help - -# -# From test -# - -# DEPRECATED: Turn off stove (boolean value) -# This option is deprecated for removal. -# Its value may be silently ignored in the future. -# Reason: This was supposed to work but it really, really did not. -# Always buy house insurance. -#turn_off_stove = false -''')), - ('deprecated_opt_with_deprecated_group', - dict(opts=[('test', - [(groups['foo'], - [opts['deprecated_opt_with_deprecated_group']])])], - expected='''[DEFAULT] - - -[foo] -# foo help - -# -# From test -# - -# deprecated (string value) -# Deprecated group/name - [group1]/foobar -#bar = -''')), - ('unknown_type', - dict(opts=[('test', [(None, [opts['unknown_type']])])], - expected='''[DEFAULT] - -# -# From test -# - -# unknown (unknown type) -#unknown_opt = 123 -''')), - ('str_opt', - dict(opts=[('test', [(None, [opts['str_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a string (string value) -#str_opt = foo bar -''')), - ('str_opt_with_space', - dict(opts=[('test', [(None, [opts['str_opt_with_space']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a string with spaces (string value) -#str_opt = " foo bar " -''')), - ('bool_opt', - dict(opts=[('test', [(None, [opts['bool_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a boolean (boolean value) -#bool_opt = false -''')), - ('int_opt', - dict(opts=[('test', [(None, [opts['int_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# an integer (integer value) -# Minimum value: 1 -# Maximum value: 20 -#int_opt = 10 -''')), - ('int_opt_min_0', - dict(opts=[('test', [(None, [opts['int_opt_min_0']])])], - expected='''[DEFAULT] - -# -# From test -# - -# an integer (integer value) -# Minimum value: 0 -# Maximum value: 20 -#int_opt_min_0 = 10 -''')), - ('int_opt_max_0', - dict(opts=[('test', [(None, [opts['int_opt_max_0']])])], - expected='''[DEFAULT] - -# -# From test -# - -# an integer (integer value) -# Maximum value: 0 -#int_opt_max_0 = -1 -''')), - - ('float_opt', - dict(opts=[('test', [(None, [opts['float_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a float (floating point value) -#float_opt = 0.1 -''')), - ('list_opt', - dict(opts=[('test', [(None, [opts['list_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a list (list value) -#list_opt = 1,2,3 -''')), - ('dict_opt', - dict(opts=[('test', [(None, [opts['dict_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# 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: 0 -# Maximum value: 65535 -#port_opt = 80 -''')), - ('hostname_opt', - dict(opts=[('test', [(None, [opts['hostname_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a hostname (hostname value) -#hostname_opt = compute01.nova.site1 -''')), - ('multi_opt', - dict(opts=[('test', [(None, [opts['multi_opt']])])], - expected='''[DEFAULT] - -# -# From test -# - -# multiple strings (multi valued) -#multi_opt = 1 -#multi_opt = 2 -#multi_opt = 3 -''')), - ('multi_opt_none', - dict(opts=[('test', [(None, [opts['multi_opt_none']])])], - expected='''[DEFAULT] - -# -# From test -# - -# multiple strings (multi valued) -#multi_opt_none = -''')), - ('multi_opt_empty', - dict(opts=[('test', [(None, [opts['multi_opt_empty']])])], - expected='''[DEFAULT] - -# -# From test -# - -# multiple strings (multi valued) -#multi_opt_empty = -''')), - ('str_opt_sample_default', - dict(opts=[('test', [(None, [opts['str_opt_sample_default']])])], - expected='''[DEFAULT] - -# -# From test -# - -# a string (string value) -#str_opt = fooishbar -''')), - ('native_str_type', - dict(opts=[('test', [(None, [opts['native_str_type']])])], - expected='''[DEFAULT] - -# -# From test -# - -# native help (string value) -#native_str_type = -''')), - ('native_int_type', - dict(opts=[('test', [(None, [opts['native_int_type']])])], - expected='''[DEFAULT] - -# -# From test -# - -# native help (integer value) -#native_int_type = -''')), - ('native_float_type', - dict(opts=[('test', [(None, [opts['native_float_type']])])], - expected='''[DEFAULT] - -# -# From test -# - -# native help (floating point value) -#native_float_type = -''')), - ('multi_opt_sample_default', - dict(opts=[('test', [(None, [opts['multi_opt_sample_default']])])], - expected='''[DEFAULT] - -# -# From test -# - -# multiple strings (multi valued) -#multi_opt = 5 -#multi_opt = 6 -''')), - ('custom_type_name', - dict(opts=[('test', [(None, [opts['custom_type_name']])])], - expected='''[DEFAULT] - -# -# From test -# - -# this is a port (port number) -#custom_opt_type = 5511 -''')), - ('custom_type', - dict(opts=[('test', [(None, [opts['custom_type']])])], - expected='''[DEFAULT] - -# -# From test -# - -# custom help (unknown value) -#custom_type = -''')), - ('string_type_with_bad_default', - dict(opts=[('test', [(None, - [opts['string_type_with_bad_default']])])], - expected='''[DEFAULT] - -# -# From test -# - -# string with bad default (string value) -#string_type_with_bad_default = 4096 -''')), - ('str_opt_str_group', - dict(opts=[('test', [('foo', - [opts['str_opt']]), - (groups['foo'], - [opts['int_opt']])]), - ('foo', [('foo', - [opts['bool_opt']])])], - expected='''[DEFAULT] - - -[foo] -# foo help - -# -# From foo -# - -# a boolean (boolean value) -#bool_opt = false - -# -# From test -# - -# a string (string value) -#str_opt = foo bar - -# -# From test -# - -# an integer (integer value) -# Minimum value: 1 -# Maximum value: 20 -#int_opt = 10 -''')), - ('opt_str_opt_group', - dict(opts=[('test', [(groups['foo'], - [opts['int_opt']]), - ('foo', - [opts['str_opt']])]), - ('foo', [(groups['foo'], - [opts['bool_opt']])])], - expected='''[DEFAULT] - - -[foo] -# foo help - -# -# From foo -# - -# a boolean (boolean value) -#bool_opt = false - -# -# From test -# - -# an integer (integer value) -# Minimum value: 1 -# Maximum value: 20 -#int_opt = 10 - -# -# From test -# - -# a string (string value) -#str_opt = foo bar -''')), - ] - - output_file_scenarios = [ - ('stdout', - dict(stdout=True, output_file=None)), - ('output_file', - dict(output_file='sample.conf', stdout=False)), - ] - - @classmethod - def generate_scenarios(cls): - cls.scenarios = testscenarios.multiply_scenarios( - cls.content_scenarios, - cls.output_file_scenarios) - - def setUp(self): - super(GeneratorTestCase, self).setUp() - - self.conf = cfg.ConfigOpts() - self.config_fixture = config_fixture.Config(self.conf) - self.config = self.config_fixture.config - self.useFixture(self.config_fixture) - - self.tempdir = self.useFixture(fixtures.TempDir()) - - def _capture_stream(self, stream_name): - self.useFixture(fixtures.MonkeyPatch("sys.%s" % stream_name, - moves.StringIO())) - return getattr(sys, stream_name) - - def _capture_stdout(self): - return self._capture_stream('stdout') - - @mock.patch.object(generator, '_get_raw_opts_loaders') - @mock.patch.object(generator, 'LOG') - def test_generate(self, mock_log, raw_opts_loader): - generator.register_cli_opts(self.conf) - - namespaces = [i[0] for i in self.opts] - self.config(namespace=namespaces) - - for group in self.groups.values(): - self.conf.register_group(group) - - wrap_width = getattr(self, 'wrap_width', None) - if wrap_width is not None: - self.config(wrap_width=wrap_width) - - if self.stdout: - stdout = self._capture_stdout() - else: - output_file = self.tempdir.join(self.output_file) - self.config(output_file=output_file) - - # We have a static data structure matching what should be - # returned by _list_opts() but we're mocking out a lower level - # function that needs to return a namespace and a callable to - # return options from that namespace. We have to pass opts to - # the lambda to cache a reference to the name because the list - # comprehension changes the thing pointed to by the name each - # time through the loop. - raw_opts_loader.return_value = [ - (ns, lambda opts=opts: opts) - for ns, opts in self.opts - ] - - generator.generate(self.conf) - - if self.stdout: - self.assertEqual(self.expected, stdout.getvalue()) - else: - with open(output_file, 'r') as f: - actual = f.read() - self.assertEqual(self.expected, actual) - - log_warning = getattr(self, 'log_warning', None) - if log_warning is not None: - mock_log.warning.assert_called_once_with(*log_warning) - else: - self.assertFalse(mock_log.warning.called) - - -class IgnoreDoublesTestCase(base.BaseTestCase): - - opts = [cfg.StrOpt('foo', help='foo option'), - cfg.StrOpt('bar', help='bar option'), - cfg.StrOpt('foo_bar', help='foobar'), - cfg.StrOpt('str_opt', help='a string'), - cfg.BoolOpt('bool_opt', help='a boolean'), - cfg.IntOpt('int_opt', help='an integer')] - - def test_cleanup_opts_default(self): - o = [("namespace1", [ - ("group1", self.opts)])] - self.assertEqual(o, generator._cleanup_opts(o)) - - def test_cleanup_opts_dup_opt(self): - o = [("namespace1", [ - ("group1", self.opts + [self.opts[0]])])] - e = [("namespace1", [ - ("group1", self.opts)])] - self.assertEqual(e, generator._cleanup_opts(o)) - - def test_cleanup_opts_dup_groups_opt(self): - o = [("namespace1", [ - ("group1", self.opts + [self.opts[1]]), - ("group2", self.opts), - ("group3", self.opts + [self.opts[2]])])] - e = [("namespace1", [ - ("group1", self.opts), - ("group2", self.opts), - ("group3", self.opts)])] - self.assertEqual(e, generator._cleanup_opts(o)) - - def test_cleanup_opts_dup_namespace_groups_opts(self): - o = [("namespace1", [ - ("group1", self.opts + [self.opts[1]]), - ("group2", self.opts)]), - ("namespace2", [ - ("group1", self.opts + [self.opts[2]]), - ("group2", self.opts)])] - e = [("namespace1", [ - ("group1", self.opts), - ("group2", self.opts)]), - ("namespace2", [ - ("group1", self.opts), - ("group2", self.opts)])] - self.assertEqual(e, generator._cleanup_opts(o)) - - @mock.patch.object(generator, '_get_raw_opts_loaders') - def test_list_ignores_doubles(self, raw_opts_loaders): - config_opts = [ - (None, [cfg.StrOpt('foo'), cfg.StrOpt('bar')]), - ] - - # These are the very same config options, but read twice. - # This is possible if one misconfigures the entry point for the - # sample config generator. - raw_opts_loaders.return_value = [ - ('namespace', lambda: config_opts), - ('namespace', lambda: config_opts), - ] - - slurped_opts = 0 - for _, listing in generator._list_opts(['namespace']): - for _, opts in listing: - slurped_opts += len(opts) - self.assertEqual(2, slurped_opts) - - -class GeneratorAdditionalTestCase(base.BaseTestCase): - - opts = [cfg.StrOpt('foo', help='foo option', default='fred'), - cfg.StrOpt('bar', help='bar option'), - cfg.StrOpt('foo_bar', help='foobar'), - cfg.StrOpt('str_opt', help='a string'), - cfg.BoolOpt('bool_opt', help='a boolean'), - cfg.IntOpt('int_opt', help='an integer')] - - def test_get_groups_empty_ns(self): - groups = generator._get_groups([]) - self.assertEqual({'DEFAULT': {'object': None, 'namespaces': []}}, - groups) - - def test_get_groups_single_ns(self): - config = [("namespace1", [ - ("beta", self.opts), - ("alpha", self.opts)])] - groups = generator._get_groups(config) - self.assertEqual(['DEFAULT', 'alpha', 'beta'], sorted(groups)) - - def test_get_groups_multiple_ns(self): - config = [("namespace1", [ - ("beta", self.opts), - ("alpha", self.opts)]), - ("namespace2", [ - ("gamma", self.opts), - ("alpha", self.opts)])] - groups = generator._get_groups(config) - self.assertEqual(['DEFAULT', 'alpha', 'beta', 'gamma'], sorted(groups)) - - def test_output_opts_empty_default(self): - - config = [("namespace1", [ - ("alpha", [])])] - groups = generator._get_groups(config) - - fd, tmp_file = tempfile.mkstemp() - with open(tmp_file, 'w+') as f: - formatter = generator._OptFormatter(output_file=f) - generator._output_opts(formatter, 'DEFAULT', groups.pop('DEFAULT')) - expected = '''[DEFAULT] -''' - with open(tmp_file, 'r') as f: - actual = f.read() - self.assertEqual(expected, actual) - - def test_output_opts_group(self): - - config = [("namespace1", [ - ("alpha", [self.opts[0]])])] - groups = generator._get_groups(config) - - fd, tmp_file = tempfile.mkstemp() - with open(tmp_file, 'w+') as f: - formatter = generator._OptFormatter(output_file=f) - generator._output_opts(formatter, 'alpha', groups.pop('alpha')) - expected = '''[alpha] - -# -# From namespace1 -# - -# foo option (string value) -#foo = fred -''' - with open(tmp_file, 'r') as f: - actual = f.read() - self.assertEqual(expected, actual) - - def _test_output_default_list_opt_with_string_value(self, default): - opt = cfg.ListOpt('list_opt', help='a list', default=default) - config = [("namespace1", [ - ("alpha", [opt])])] - groups = generator._get_groups(config) - - fd, tmp_file = tempfile.mkstemp() - f = open(tmp_file, 'w+') - formatter = generator._OptFormatter(output_file=f) - expected = '''[alpha] - -# -# From namespace1 -# - -# a list (list value) -#list_opt = %(default)s -''' % {'default': default} - generator._output_opts(formatter, 'alpha', groups.pop('alpha')) - f.close() - content = open(tmp_file).read() - self.assertEqual(expected, content) - - def test_output_default_list_opt_with_string_value_multiple_entries(self): - self._test_output_default_list_opt_with_string_value('foo,bar') - - def test_output_default_list_opt_with_string_value_single_entry(self): - self._test_output_default_list_opt_with_string_value('foo') - - -class GeneratorMutableOptionTestCase(base.BaseTestCase): - - def test_include_message(self): - out = moves.StringIO() - opt = cfg.StrOpt('foo', help='foo option', mutable=True) - gen = generator._OptFormatter(output_file=out) - gen.format(opt, 'group1') - result = out.getvalue() - self.assertIn( - 'This option can be changed without restarting.', - result, - ) - - def test_do_not_include_message(self): - out = moves.StringIO() - opt = cfg.StrOpt('foo', help='foo option', mutable=False) - gen = generator._OptFormatter(output_file=out) - gen.format(opt, 'group1') - result = out.getvalue() - self.assertNotIn( - 'This option can be changed without restarting.', - result, - ) - - -class GeneratorRaiseErrorTestCase(base.BaseTestCase): - - def test_generator_raises_error(self): - """Verifies that errors from extension manager are not suppressed.""" - class FakeException(Exception): - pass - - class FakeEP(object): - - def __init__(self): - self.name = 'callback_is_expected' - self.require = self.resolve - self.load = self.resolve - - def resolve(self, *args, **kwargs): - raise FakeException() - - fake_ep = FakeEP() - self.conf = cfg.ConfigOpts() - self.conf.register_opts(generator._generator_opts) - self.conf.set_default('namespace', fake_ep.name) - fake_eps = mock.Mock(return_value=[fake_ep]) - with mock.patch('pkg_resources.iter_entry_points', fake_eps): - self.assertRaises(FakeException, generator.generate, self.conf) - - def test_generator_call_with_no_arguments_raises_error(self): - testargs = ['oslo-config-generator'] - with mock.patch('sys.argv', testargs): - self.assertRaises(cfg.RequiredOptError, generator.main, []) - - -class ChangeDefaultsTestCase(base.BaseTestCase): - - @mock.patch.object(generator, '_get_opt_default_updaters') - @mock.patch.object(generator, '_get_raw_opts_loaders') - def test_no_modifiers_registered(self, raw_opts_loaders, get_updaters): - orig_opt = cfg.StrOpt('foo', default='bar') - raw_opts_loaders.return_value = [ - ('namespace', lambda: [(None, [orig_opt])]), - ] - get_updaters.return_value = [] - - opts = generator._list_opts(['namespace']) - # NOTE(dhellmann): Who designed this data structure? - the_opt = opts[0][1][0][1][0] - - self.assertEqual('bar', the_opt.default) - self.assertIs(orig_opt, the_opt) - - @mock.patch.object(generator, '_get_opt_default_updaters') - @mock.patch.object(generator, '_get_raw_opts_loaders') - def test_change_default(self, raw_opts_loaders, get_updaters): - orig_opt = cfg.StrOpt('foo', default='bar') - raw_opts_loaders.return_value = [ - ('namespace', lambda: [(None, [orig_opt])]), - ] - - def updater(): - cfg.set_defaults([orig_opt], foo='blah') - - get_updaters.return_value = [updater] - - opts = generator._list_opts(['namespace']) - # NOTE(dhellmann): Who designed this data structure? - the_opt = opts[0][1][0][1][0] - - self.assertEqual('blah', the_opt.default) - self.assertIs(orig_opt, the_opt) - - -class RequiredOptionTestCase(base.BaseTestCase): - - opts = [cfg.StrOpt('foo', help='foo option', default='fred'), - cfg.StrOpt('bar', help='bar option', required=True), - cfg.StrOpt('foo_bar', help='foobar'), - cfg.StrOpt('bars', help='bars foo', required=True)] - - def test_required_option_order_single_ns(self): - - config = [("namespace1", [ - ("alpha", self.opts)])] - groups = generator._get_groups(config) - - fd, tmp_file = tempfile.mkstemp() - with open(tmp_file, 'w+') as f: - formatter = generator._OptFormatter(output_file=f) - generator._output_opts(formatter, 'alpha', - groups.pop('alpha'), True) - expected = '''[alpha] - -# -# From namespace1 -# - -# bar option (string value) -bar = - -# bars foo (string value) -bars = -''' - with open(tmp_file, 'r') as f: - actual = f.read() - self.assertEqual(expected, actual) - - -class AdvancedOptionsTestCase(base.BaseTestCase): - - opts = [cfg.StrOpt('foo', help='foo option', default='fred'), - cfg.StrOpt('bar', help='bar option', advanced=True), - cfg.StrOpt('foo_bar', help='foobar'), - cfg.BoolOpt('bars', help='bars foo', default=True, advanced=True)] - - def test_advanced_option_order_single_ns(self): - - config = [("namespace1", [ - ("alpha", self.opts)])] - groups = generator._get_groups(config) - - fd, tmp_file = tempfile.mkstemp() - with open(tmp_file, 'w+') as f: - formatter = generator._OptFormatter(output_file=f) - generator._output_opts(formatter, 'alpha', groups.pop('alpha')) - expected = '''[alpha] - -# -# From namespace1 -# - -# foo option (string value) -#foo = fred - -# foobar (string value) -#foo_bar = - -# bar option (string value) -# Advanced Option: intended for advanced users and not used -# by the majority of users, and might have a significant -# effect on stability and/or performance. -#bar = - -# bars foo (boolean value) -# Advanced Option: intended for advanced users and not used -# by the majority of users, and might have a significant -# effect on stability and/or performance. -#bars = true -''' - with open(tmp_file, 'r') as f: - actual = f.read() - self.assertEqual(expected, actual) - - -GeneratorTestCase.generate_scenarios() diff --git a/oslo_config/tests/test_iniparser.py b/oslo_config/tests/test_iniparser.py deleted file mode 100644 index b54479a..0000000 --- a/oslo_config/tests/test_iniparser.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import unittest - -from oslo_config import iniparser - - -class TestParser(iniparser.BaseParser): - comment_called = False - values = None - section = '' - - def __init__(self): - self.values = {} - - def assignment(self, key, value): - self.values.setdefault(self.section, {}) - self.values[self.section][key] = value - - def new_section(self, section): - self.section = section - - def comment(self, section): - self.comment_called = True - - -class BaseParserTestCase(unittest.TestCase): - def setUp(self): - self.parser = iniparser.BaseParser() - - def _assertParseError(self, *lines): - self.assertRaises(iniparser.ParseError, self.parser.parse, lines) - - def test_invalid_assignment(self): - self._assertParseError("foo - bar") - - def test_empty_key(self): - self._assertParseError(": bar") - - def test_unexpected_continuation(self): - self._assertParseError(" baz") - - def test_invalid_section(self): - self._assertParseError("[section") - - def test_no_section_name(self): - self._assertParseError("[]") - - -class ParserTestCase(unittest.TestCase): - def setUp(self): - self.parser = TestParser() - - def test_blank_line(self): - lines = [""] - self.parser.parse(lines) - self.assertEqual({}, self.parser.values) - - def test_assignment_equal(self): - lines = ["foo = bar"] - self.parser.parse(lines) - self.assertEqual({'': {'foo': ['bar']}}, self.parser.values) - - def test_assignment_colon(self): - lines = ["foo: bar"] - self.parser.parse(lines) - self.assertEqual({'': {'foo': ['bar']}}, self.parser.values) - - def test_assignment_multiline(self): - lines = ["foo = bar0", " bar1"] - self.parser.parse(lines) - self.assertEqual({'': {'foo': ['bar0', 'bar1']}}, self.parser.values) - - def test_assignment_multline_empty(self): - lines = ["foo = bar0", "", " bar1"] - self.assertRaises(iniparser.ParseError, self.parser.parse, lines) - - def test_section_assignment(self): - lines = ["[test]", "foo = bar"] - self.parser.parse(lines) - self.assertEqual({'test': {'foo': ['bar']}}, self.parser.values) - - def test_new_section(self): - lines = ["[foo]"] - self.parser.parse(lines) - self.assertEqual('foo', self.parser.section) - - def test_comment(self): - lines = ["# foobar"] - self.parser.parse(lines) - self.assertTrue(self.parser.comment_called) - - def test_empty_assignment(self): - lines = ["foo = "] - self.parser.parse(lines) - self.assertEqual({'': {'foo': ['']}}, self.parser.values) - - def test_assignment_space_single_quote(self): - lines = ["foo = ' bar '"] - self.parser.parse(lines) - self.assertEqual({'': {'foo': [' bar ']}}, self.parser.values) - - def test_assignment_space_double_quote(self): - lines = ["foo = \" bar \""] - self.parser.parse(lines) - self.assertEqual({'': {'foo': [' bar ']}}, self.parser.values) - - -class ExceptionTestCase(unittest.TestCase): - def test_parseerror(self): - exc = iniparser.ParseError('test', 42, 'example') - self.assertEqual(str(exc), "at line 42, test: 'example'") diff --git a/oslo_config/tests/test_sphinxconfiggen.py b/oslo_config/tests/test_sphinxconfiggen.py deleted file mode 100644 index f28c731..0000000 --- a/oslo_config/tests/test_sphinxconfiggen.py +++ /dev/null @@ -1,128 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -import mock -from oslotest import base - -from oslo_config import sphinxconfiggen - - -class SingleSampleGenerationTest(base.BaseTestCase): - - @mock.patch('os.path.isdir') - @mock.patch('os.path.isfile') - @mock.patch('oslo_config.generator.main') - def test_sample_gen_with_single_config_file(self, main, isfile, isdir): - isfile.side_effect = [False, True] - isdir.return_value = True - - config = mock.Mock(config_generator_config_file='nova-gen.conf', - sample_config_basename='nova') - app = mock.Mock(srcdir='/opt/nova', config=config) - sphinxconfiggen.generate_sample(app) - - main.assert_called_once_with(args=['--config-file', - '/opt/nova/nova-gen.conf', - '--output-file', - '/opt/nova/nova.conf.sample' - ]) - - @mock.patch('os.path.isdir') - @mock.patch('os.path.isfile') - @mock.patch('oslo_config.generator.main') - def test_sample_gen_with_single_config_file_no_base(self, main, isfile, - isdir): - isfile.side_effect = [False, True] - isdir.return_value = True - - config = mock.Mock(config_generator_config_file='nova-gen.conf', - sample_config_basename=None) - app = mock.Mock(srcdir='/opt/nova', config=config) - sphinxconfiggen.generate_sample(app) - - main.assert_called_once_with(args=['--config-file', - '/opt/nova/nova-gen.conf', - '--output-file', - '/opt/nova/sample.config']) - - -class MultipleSampleGenerationTest(base.BaseTestCase): - - @mock.patch('os.path.isdir') - @mock.patch('os.path.isfile') - @mock.patch('oslo_config.generator.main') - def test_multi_sample_gen(self, main, isfile, isdir): - isfile.side_effect = [False, True, False, True] - isdir.return_value = True - - multiple_configs = [('glance-api-gen.conf', 'glance-api'), - ('glance-reg-gen.conf', 'glance-reg')] - config = mock.Mock(config_generator_config_file=multiple_configs) - app = mock.Mock(srcdir='/opt/glance', config=config) - sphinxconfiggen.generate_sample(app) - - self.assertEqual(main.call_count, 2) - main.assert_any_call(args=['--config-file', - '/opt/glance/glance-api-gen.conf', - '--output-file', - '/opt/glance/glance-api.conf.sample']) - main.assert_any_call(args=['--config-file', - '/opt/glance/glance-reg-gen.conf', - '--output-file', - '/opt/glance/glance-reg.conf.sample']) - - @mock.patch('os.path.isdir') - @mock.patch('os.path.isfile') - @mock.patch('oslo_config.generator.main') - def test_multi_sample_gen_with_without_one_base(self, main, isfile, isdir): - isfile.side_effect = [False, True, False, True] - isdir.return_value = True - - multiple_configs = [('glance-api-gen.conf', 'glance-api'), - ('glance-reg-gen.conf', None)] - config = mock.Mock(config_generator_config_file=multiple_configs) - app = mock.Mock(srcdir='/opt/glance', config=config) - sphinxconfiggen.generate_sample(app) - - self.assertEqual(main.call_count, 2) - main.assert_any_call(args=['--config-file', - '/opt/glance/glance-api-gen.conf', - '--output-file', - '/opt/glance/glance-api.conf.sample']) - main.assert_any_call(args=['--config-file', - '/opt/glance/glance-reg-gen.conf', - '--output-file', - '/opt/glance/glance-reg-gen.conf.sample']) - - @mock.patch('os.path.isdir') - @mock.patch('os.path.isfile') - @mock.patch('oslo_config.generator.main') - def test_multi_sample_gen_with_without_any_base(self, main, isfile, isdir): - isfile.side_effect = [False, True, False, True] - isdir.return_value = True - - multiple_configs = [('glance-api-gen.conf', None), - ('glance-reg-gen.conf', None)] - config = mock.Mock(config_generator_config_file=multiple_configs) - app = mock.Mock(srcdir='/opt/glance', config=config) - sphinxconfiggen.generate_sample(app) - - self.assertEqual(main.call_count, 2) - main.assert_any_call(args=['--config-file', - '/opt/glance/glance-api-gen.conf', - '--output-file', - '/opt/glance/glance-api-gen.conf.sample']) - main.assert_any_call(args=['--config-file', - '/opt/glance/glance-reg-gen.conf', - '--output-file', - '/opt/glance/glance-reg-gen.conf.sample']) diff --git a/oslo_config/tests/test_sphinxext.py b/oslo_config/tests/test_sphinxext.py deleted file mode 100644 index 03e7cbb..0000000 --- a/oslo_config/tests/test_sphinxext.py +++ /dev/null @@ -1,483 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import textwrap - -import mock -from oslotest import base - -from oslo_config import cfg -from oslo_config import sphinxext - - -class FormatGroupTest(base.BaseTestCase): - - def test_none_in_default(self): - # option with None group placed in DEFAULT - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - help='this appears in the default group'), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - - this appears in the default group - - ''').lstrip(), results) - - def test_with_default_value(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - default='this is the default', - help='this appears in the default group'), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ``this is the default`` - - this appears in the default group - - ''').lstrip(), results) - - def test_with_min(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.IntOpt('opt_name', - min=1), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: integer - :Default: ```` - :Minimum Value: 1 - - ''').lstrip(), results) - - def test_with_min_0(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.IntOpt('opt_name', - min=0), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: integer - :Default: ```` - :Minimum Value: 0 - - ''').lstrip(), results) - - def test_with_max(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.IntOpt('opt_name', - max=1), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: integer - :Default: ```` - :Maximum Value: 1 - - ''').lstrip(), results) - - def test_with_max_0(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.IntOpt('opt_name', - max=0), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: integer - :Default: ```` - :Maximum Value: 0 - - ''').lstrip(), results) - - def test_with_choices(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - choices=['a', 'b', 'c', None, '']), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - :Valid Values: a, b, c, , '' - - ''').lstrip(), results) - - def test_group_obj_without_help(self): - # option with None group placed in DEFAULT - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name='group', - group_obj=cfg.OptGroup('group'), - opt_list=[cfg.StrOpt('opt_name')], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: group - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - - ''').lstrip(), results) - - def test_group_obj_with_help(self): - # option with None group placed in DEFAULT - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name='group', - group_obj=cfg.OptGroup('group', help='group help'), - opt_list=[cfg.StrOpt('opt_name')], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: group - - group help - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - - ''').lstrip(), results) - - def test_deprecated_opts_without_deprecated_group(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - deprecated_name='deprecated_name', - ) - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - - .. list-table:: Deprecated Variations - :header-rows: 1 - - - * Group - * Name - - * DEFAULT - * deprecated_name - - ''').lstrip(), results) - - def test_deprecated_opts_with_deprecated_group(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - deprecated_name='deprecated_name', - deprecated_group='deprecated_group', - ) - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - - .. list-table:: Deprecated Variations - :header-rows: 1 - - - * Group - * Name - - * deprecated_group - * deprecated_name - - ''').lstrip(), results) - - def test_deprecated_for_removal(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - deprecated_for_removal=True, - deprecated_reason='because I said so', - deprecated_since='13.0', - ) - ], - ))) - self.assertIn('.. warning::', results) - self.assertIn('because I said so', results) - self.assertIn('since 13.0', results) - - def test_mutable(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.IntOpt('opt_name', - mutable=True), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: integer - :Default: ```` - :Mutable: This option can be changed without restarting. - - ''').lstrip(), results) - - def test_not_mutable(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.IntOpt('opt_name', - mutable=False), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: integer - :Default: ```` - - ''').lstrip(), results) - - def test_advanced(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - advanced=True), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - :Advanced Option: intended for advanced users and not used - :by the majority of users, and might have a significant - :effect on stability and/or performance. - - ''').lstrip(), results) - - def test_not_advanced(self): - results = '\n'.join(list(sphinxext._format_group( - app=mock.Mock(), - namespace=None, - group_name=None, - group_obj=None, - opt_list=[ - cfg.StrOpt('opt_name', - advanced=False), - ], - ))) - self.assertEqual(textwrap.dedent(''' - .. oslo.config:group:: DEFAULT - - .. oslo.config:option:: opt_name - - :Type: string - :Default: ```` - - ''').lstrip(), results) - - -class FormatOptionHelpTest(base.BaseTestCase): - - @mock.patch('oslo_config.generator._list_opts') - @mock.patch('oslo_config.sphinxext._format_group') - def test_split_namespaces(self, _format_group, _list_opts): - _list_opts.return_value = [ - ('namespace1', [(None, ['opt1'])]), - ('namespace2', [(None, ['opt2'])]), - ] - list(sphinxext._format_option_help( - app=None, - namespaces=['namespace1', 'namespace2'], - split_namespaces=True)) - _format_group.assert_any_call( - app=None, - namespace='namespace1', - group_name=None, - group_obj=None, - opt_list=['opt1'], - ) - _format_group.assert_any_call( - app=None, - namespace='namespace2', - group_name=None, - group_obj=None, - opt_list=['opt2'], - ) - - @mock.patch('oslo_config.generator._list_opts') - @mock.patch('oslo_config.sphinxext._format_group') - def test_dont_split_namespaces(self, _format_group, _list_opts): - _list_opts.return_value = [ - ('namespace1', [(None, ['opt1'])]), - ('namespace2', [(None, ['opt2'])]), - ] - list(sphinxext._format_option_help( - app=None, - namespaces=['namespace1', 'namespace2'], - split_namespaces=False)) - _format_group.assert_called_once_with( - app=None, - namespace=None, - group_name=None, - group_obj=None, - opt_list=['opt1', 'opt2'], - ) - - @mock.patch('oslo_config.generator._list_opts') - @mock.patch('oslo_config.sphinxext._format_group') - def test_dont_split_namespaces_with_group(self, _format_group, _list_opts): - grp_obj = cfg.OptGroup('grp1') - _list_opts.return_value = [ - ('namespace1', [(grp_obj, ['opt1'])]), - ('namespace2', [('grp1', ['opt2'])]), - ] - list(sphinxext._format_option_help( - app=None, - namespaces=['namespace1', 'namespace2'], - split_namespaces=False)) - _format_group.assert_any_call( - app=None, - namespace=None, - group_name='grp1', - group_obj=grp_obj, - opt_list=['opt1', 'opt2'], - ) - - @mock.patch('oslo_config.generator._list_opts') - @mock.patch('oslo_config.sphinxext._format_group') - def test_split_namespaces_with_group(self, _format_group, _list_opts): - grp_obj = cfg.OptGroup('grp1') - _list_opts.return_value = [ - ('namespace1', [(grp_obj, ['opt1'])]), - ('namespace2', [('grp1', ['opt2'])]), - ] - list(sphinxext._format_option_help( - app=None, - namespaces=['namespace1', 'namespace2'], - split_namespaces=True)) - print(_format_group.call_args_list) - _format_group.assert_any_call( - app=None, - namespace='namespace1', - group_name='grp1', - group_obj=grp_obj, - opt_list=['opt1'], - ) - _format_group.assert_any_call( - app=None, - namespace='namespace2', - group_name='grp1', - group_obj=None, - opt_list=['opt2'], - ) diff --git a/oslo_config/tests/test_types.py b/oslo_config/tests/test_types.py deleted file mode 100644 index f2d429e..0000000 --- a/oslo_config/tests/test_types.py +++ /dev/null @@ -1,884 +0,0 @@ -# Copyright 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import re -import unittest - -from oslo_config import types - - -class ConfigTypeTests(unittest.TestCase): - def test_none_concrete_class(self): - class MyString(types.ConfigType): - def __init__(self, type_name='mystring value'): - super(MyString, self).__init__(type_name=type_name) - - self.assertRaises(TypeError, MyString) - - def test_concrete_class(self): - class MyString(types.ConfigType): - def __init__(self, type_name='mystring value'): - super(MyString, self).__init__(type_name=type_name) - - def _formatter(self, value): - return value - - MyString() - - -class TypeTestHelper(object): - def setUp(self): - super(TypeTestHelper, self).setUp() - self.type_instance = self.type - - def assertConvertedValue(self, s, expected): - self.assertEqual(expected, self.type_instance(s)) - - def assertInvalid(self, value): - self.assertRaises(ValueError, self.type_instance, value) - - -class StringTypeTests(TypeTestHelper, unittest.TestCase): - type = types.String() - - def test_empty_string_passes(self): - self.assertConvertedValue('', '') - - def test_should_return_same_string_if_valid(self): - self.assertConvertedValue('foo bar', 'foo bar') - - def test_listed_value(self): - self.type_instance = types.String(choices=['foo', 'bar']) - self.assertConvertedValue('foo', 'foo') - - def test_unlisted_value(self): - self.type_instance = types.String(choices=['foo', 'bar']) - self.assertInvalid('baz') - - def test_with_no_values_returns_error(self): - self.type_instance = types.String(choices=[]) - self.assertInvalid('foo') - - def test_string_with_non_closed_quote_is_invalid(self): - self.type_instance = types.String(quotes=True) - self.assertInvalid('"foo bar') - self.assertInvalid("'bar baz") - - def test_quotes_are_stripped(self): - self.type_instance = types.String(quotes=True) - self.assertConvertedValue('"foo bar"', 'foo bar') - - def test_trailing_quote_is_ok(self): - self.type_instance = types.String(quotes=True) - self.assertConvertedValue('foo bar"', 'foo bar"') - - def test_repr(self): - t = types.String() - self.assertEqual('String', repr(t)) - - def test_repr_with_choices(self): - t = types.String(choices=['foo', 'bar']) - self.assertEqual('String(choices=[\'foo\', \'bar\'])', repr(t)) - - def test_equal(self): - self.assertTrue(types.String() == types.String()) - - def test_equal_with_same_choices(self): - t1 = types.String(choices=['foo', 'bar']) - t2 = types.String(choices=['foo', 'bar']) - t3 = types.String(choices=('foo', 'bar')) - t4 = types.String(choices=['bar', 'foo']) - self.assertTrue(t1 == t2) - self.assertTrue(t1 == t3) - self.assertTrue(t1 == t4) - - def test_not_equal_with_different_choices(self): - t1 = types.String(choices=['foo', 'bar']) - t2 = types.String(choices=['foo', 'baz']) - self.assertFalse(t1 == t2) - - def test_equal_with_equal_quote_falgs(self): - t1 = types.String(quotes=True) - t2 = types.String(quotes=True) - self.assertTrue(t1 == t2) - - def test_not_equal_with_different_quote_falgs(self): - t1 = types.String(quotes=False) - t2 = types.String(quotes=True) - self.assertFalse(t1 == t2) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.String() == types.Integer()) - - def test_regex_matches(self): - self.type_instance = types.String(regex=re.compile("^[A-Z]")) - self.assertConvertedValue("Foo", "Foo") - - def test_regex_matches_uncompiled(self): - self.type_instance = types.String(regex="^[A-Z]") - self.assertConvertedValue("Foo", "Foo") - - def test_regex_fails(self): - self.type_instance = types.String(regex=re.compile("^[A-Z]")) - self.assertInvalid("foo") - - def test_regex_and_choices_raises(self): - self.assertRaises(ValueError, - types.String, - regex=re.compile("^[A-Z]"), - choices=["Foo", "Bar", "baz"]) - - def test_equal_with_same_regex(self): - t1 = types.String(regex=re.compile("^[A-Z]")) - t2 = types.String(regex=re.compile("^[A-Z]")) - self.assertTrue(t1 == t2) - - def test_not_equal_with_different_regex(self): - t1 = types.String(regex=re.compile("^[A-Z]")) - 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() - - def test_True(self): - self.assertConvertedValue('True', True) - - def test_yes(self): - self.assertConvertedValue('yes', True) - - def test_on(self): - self.assertConvertedValue('on', True) - - def test_1(self): - self.assertConvertedValue('1', True) - - def test_False(self): - self.assertConvertedValue('False', False) - - def test_no(self): - self.assertConvertedValue('no', False) - - def test_off(self): - self.assertConvertedValue('off', False) - - def test_0(self): - self.assertConvertedValue('0', False) - - def test_other_values_produce_error(self): - self.assertInvalid('foo') - - def test_repr(self): - self.assertEqual('Boolean', repr(types.Boolean())) - - def test_equal(self): - self.assertEqual(types.Boolean(), types.Boolean()) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.Boolean() == types.String()) - - -class IntegerTypeTests(TypeTestHelper, unittest.TestCase): - type = types.Integer() - - def test_empty_string(self): - self.assertConvertedValue('', None) - - def test_whitespace_string(self): - self.assertConvertedValue(" \t\t\t\t", None) - - def test_positive_values_are_valid(self): - self.assertConvertedValue('123', 123) - - def test_zero_is_valid(self): - self.assertConvertedValue('0', 0) - - def test_negative_values_are_valid(self): - self.assertConvertedValue('-123', -123) - - def test_leading_whitespace_is_ignored(self): - self.assertConvertedValue(' 5', 5) - - def test_trailing_whitespace_is_ignored(self): - self.assertConvertedValue('7 ', 7) - - def test_non_digits_are_invalid(self): - self.assertInvalid('12a45') - - def test_repr(self): - t = types.Integer() - self.assertEqual('Integer', repr(t)) - - def test_repr_with_min(self): - t = types.Integer(min=123) - self.assertEqual('Integer(min=123)', repr(t)) - - def test_repr_with_max(self): - t = types.Integer(max=456) - self.assertEqual('Integer(max=456)', repr(t)) - - 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_repr_with_choices(self): - t = types.Integer(choices=[80, 457]) - self.assertEqual('Integer(choices=[80, 457])', repr(t)) - - def test_equal(self): - self.assertTrue(types.Integer() == types.Integer()) - - def test_equal_with_same_min_and_no_max(self): - self.assertTrue(types.Integer(min=123) == types.Integer(min=123)) - - def test_equal_with_same_max_and_no_min(self): - self.assertTrue(types.Integer(max=123) == types.Integer(max=123)) - - def test_equal_with_same_min_and_max(self): - t1 = types.Integer(min=1, max=123) - t2 = types.Integer(min=1, max=123) - self.assertTrue(t1 == t2) - - def test_equal_with_same_choices(self): - t1 = types.Integer(choices=[80, 457]) - t2 = types.Integer(choices=[457, 80]) - self.assertTrue(t1 == t2) - - def test_not_equal(self): - self.assertFalse(types.Integer(min=123) == types.Integer(min=456)) - self.assertFalse(types.Integer(choices=[80, 457]) == - types.Integer(choices=[80, 40])) - self.assertFalse(types.Integer(choices=[80, 457]) == - types.Integer()) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.Integer() == types.String()) - - def test_choices_with_min_max(self): - self.assertRaises(ValueError, - types.Integer, - min=100, - choices=[50, 60]) - self.assertRaises(ValueError, - types.Integer, - max=10, - choices=[50, 60]) - types.Integer(min=10, max=100, choices=[50, 60]) - - 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) - t(123) - t(300) - t(456) - 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) - - def test_with_choices_list(self): - t = types.Integer(choices=[80, 457]) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) - - def test_with_choices_tuple(self): - t = types.Integer(choices=(80, 457)) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) - - -class FloatTypeTests(TypeTestHelper, unittest.TestCase): - type = types.Float() - - def test_decimal_format(self): - v = self.type_instance('123.456') - self.assertAlmostEqual(v, 123.456) - - def test_decimal_format_negative_float(self): - v = self.type_instance('-123.456') - self.assertAlmostEqual(v, -123.456) - - def test_exponential_format(self): - v = self.type_instance('123e-2') - self.assertAlmostEqual(v, 1.23) - - def test_non_float_is_invalid(self): - self.assertInvalid('123,345') - self.assertInvalid('foo') - - def test_repr(self): - self.assertEqual('Float', repr(types.Float())) - - def test_repr_with_min(self): - t = types.Float(min=1.1) - self.assertEqual('Float(min=1.1)', repr(t)) - - def test_repr_with_max(self): - t = types.Float(max=2.2) - self.assertEqual('Float(max=2.2)', repr(t)) - - def test_repr_with_min_and_max(self): - t = types.Float(min=1.1, max=2.2) - self.assertEqual('Float(min=1.1, max=2.2)', repr(t)) - t = types.Float(min=1.0, max=2) - self.assertEqual('Float(min=1, max=2)', repr(t)) - t = types.Float(min=0, max=0) - self.assertEqual('Float(min=0, max=0)', repr(t)) - - def test_equal(self): - self.assertTrue(types.Float() == types.Float()) - - def test_equal_with_same_min_and_no_max(self): - self.assertTrue(types.Float(min=123.1) == types.Float(min=123.1)) - - def test_equal_with_same_max_and_no_min(self): - self.assertTrue(types.Float(max=123.1) == types.Float(max=123.1)) - - def test_not_equal(self): - self.assertFalse(types.Float(min=123.1) == types.Float(min=456.1)) - self.assertFalse(types.Float(max=123.1) == types.Float(max=456.1)) - self.assertFalse(types.Float(min=123.1) == types.Float(max=123.1)) - self.assertFalse(types.Float(min=123.1, max=456.1) == - types.Float(min=123.1, max=456.2)) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.Float() == types.Integer()) - - def test_equal_with_same_min_and_max(self): - t1 = types.Float(min=1.1, max=2.2) - t2 = types.Float(min=1.1, max=2.2) - self.assertTrue(t1 == t2) - - def test_min_greater_max(self): - self.assertRaises(ValueError, - types.Float, - min=100.1, max=50) - self.assertRaises(ValueError, - types.Float, - min=-50, max=-100.1) - self.assertRaises(ValueError, - types.Float, - min=0.1, max=-50.0) - self.assertRaises(ValueError, - types.Float, - min=50.0, max=0.0) - - def test_with_max_and_min(self): - t = types.Float(min=123.45, max=678.9) - self.assertRaises(ValueError, t, 123) - self.assertRaises(ValueError, t, 123.1) - t(124.1) - t(300) - t(456.0) - self.assertRaises(ValueError, t, 0) - self.assertRaises(ValueError, t, 800.5) - - def test_with_min_zero(self): - t = types.Float(min=0, max=456.1) - self.assertRaises(ValueError, t, -1) - t(0.0) - t(123.1) - t(300.2) - t(456.1) - self.assertRaises(ValueError, t, -201.0) - self.assertRaises(ValueError, t, 457.0) - - def test_with_max_zero(self): - t = types.Float(min=-456.1, max=0) - self.assertRaises(ValueError, t, 1) - t(0.0) - t(-123.0) - t(-300.0) - t(-456.0) - self.assertRaises(ValueError, t, 201.0) - self.assertRaises(ValueError, t, -457.0) - - -class ListTypeTests(TypeTestHelper, unittest.TestCase): - type = types.List() - - def test_empty_value(self): - self.assertConvertedValue('', []) - - def test_single_value(self): - self.assertConvertedValue(' foo bar ', - ['foo bar']) - - def test_list_of_values(self): - self.assertConvertedValue(' foo bar, baz ', - ['foo bar', - 'baz']) - - def test_list_of_values_containing_commas(self): - self.type_instance = types.List(types.String(quotes=True)) - self.assertConvertedValue('foo,"bar, baz",bam', - ['foo', - 'bar, baz', - 'bam']) - - def test_list_of_lists(self): - self.type_instance = types.List( - types.List(types.String(), bounds=True) - ) - self.assertConvertedValue('[foo],[bar, baz],[bam]', - [['foo'], ['bar', 'baz'], ['bam']]) - - def test_list_of_custom_type(self): - self.type_instance = types.List(types.Integer()) - self.assertConvertedValue('1,2,3,5', - [1, 2, 3, 5]) - - def test_bounds_parsing(self): - self.type_instance = types.List(types.Integer(), bounds=True) - self.assertConvertedValue('[1,2,3]', [1, 2, 3]) - - def test_bounds_required(self): - self.type_instance = types.List(types.Integer(), bounds=True) - self.assertInvalid('1,2,3') - self.assertInvalid('[1,2,3') - self.assertInvalid('1,2,3]') - - def test_repr(self): - t = types.List(types.Integer()) - self.assertEqual('List of Integer', repr(t)) - - def test_equal(self): - self.assertTrue(types.List() == types.List()) - - def test_equal_with_equal_custom_item_types(self): - it1 = types.Integer() - it2 = types.Integer() - self.assertTrue(types.List(it1) == types.List(it2)) - - def test_not_equal_with_non_equal_custom_item_types(self): - it1 = types.Integer() - it2 = types.String() - self.assertFalse(it1 == it2) - self.assertFalse(types.List(it1) == types.List(it2)) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.List() == types.Integer()) - - -class DictTypeTests(TypeTestHelper, unittest.TestCase): - type = types.Dict() - - def test_empty_value(self): - self.assertConvertedValue('', {}) - - def test_single_value(self): - self.assertConvertedValue(' foo: bar ', - {'foo': 'bar'}) - - def test_dict_of_values(self): - self.assertConvertedValue(' foo: bar, baz: 123 ', - {'foo': 'bar', - 'baz': '123'}) - - def test_custom_value_type(self): - self.type_instance = types.Dict(types.Integer()) - self.assertConvertedValue('foo:123, bar: 456', - {'foo': 123, - 'bar': 456}) - - def test_dict_of_values_containing_commas(self): - self.type_instance = types.Dict(types.String(quotes=True)) - self.assertConvertedValue('foo:"bar, baz",bam:quux', - {'foo': 'bar, baz', - 'bam': 'quux'}) - - def test_dict_of_dicts(self): - self.type_instance = types.Dict( - types.Dict(types.String(), bounds=True) - ) - self.assertConvertedValue('k1:{k1:v1,k2:v2},k2:{k3:v3}', - {'k1': {'k1': 'v1', 'k2': 'v2'}, - 'k2': {'k3': 'v3'}}) - - def test_bounds_parsing(self): - self.type_instance = types.Dict(types.String(), bounds=True) - self.assertConvertedValue('{foo:bar,baz:123}', - {'foo': 'bar', - 'baz': '123'}) - - def test_bounds_required(self): - self.type_instance = types.Dict(types.String(), bounds=True) - self.assertInvalid('foo:bar,baz:123') - self.assertInvalid('{foo:bar,baz:123') - self.assertInvalid('foo:bar,baz:123}') - - def test_no_mapping_produces_error(self): - self.assertInvalid('foo,bar') - - def test_repr(self): - t = types.Dict(types.Integer()) - self.assertEqual('Dict of Integer', repr(t)) - - def test_equal(self): - self.assertTrue(types.Dict() == types.Dict()) - - def test_equal_with_equal_custom_item_types(self): - it1 = types.Integer() - it2 = types.Integer() - self.assertTrue(types.Dict(it1) == types.Dict(it2)) - - def test_not_equal_with_non_equal_custom_item_types(self): - it1 = types.Integer() - it2 = types.String() - self.assertFalse(it1 == it2) - self.assertFalse(types.Dict(it1) == types.Dict(it2)) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.Dict() == types.Integer()) - - -class IPAddressTypeTests(TypeTestHelper, unittest.TestCase): - type = types.IPAddress() - - def test_ipv4_address(self): - self.assertConvertedValue('192.168.0.1', '192.168.0.1') - - def test_ipv6_address(self): - self.assertConvertedValue('abcd:ef::1', 'abcd:ef::1') - - def test_strings(self): - self.assertInvalid('') - self.assertInvalid('foo') - - def test_numbers(self): - self.assertInvalid(1) - self.assertInvalid(-1) - self.assertInvalid(3.14) - - -class IPv4AddressTypeTests(IPAddressTypeTests): - type = types.IPAddress(4) - - def test_ipv6_address(self): - self.assertInvalid('abcd:ef::1') - - -class IPv6AddressTypeTests(IPAddressTypeTests): - type = types.IPAddress(6) - - def test_ipv4_address(self): - self.assertInvalid('192.168.0.1') - - -class HostnameTypeTests(TypeTestHelper, unittest.TestCase): - type = types.Hostname() - - def assertConvertedEqual(self, value): - self.assertConvertedValue(value, value) - - def test_empty_hostname_fails(self): - self.assertInvalid('') - - def test_should_return_same_hostname_if_valid(self): - self.assertConvertedEqual('foo.bar') - - def test_trailing_quote_is_invalid(self): - self.assertInvalid('foo.bar"') - - def test_repr(self): - self.assertEqual('Hostname', repr(types.Hostname())) - - def test_equal(self): - self.assertEqual(types.Hostname(), types.Hostname()) - - def test_not_equal_to_other_class(self): - self.assertNotEqual(types.Hostname(), types.Integer()) - self.assertNotEqual(types.Hostname(), types.String()) - - def test_invalid_characters(self): - self.assertInvalid('"host"') - self.assertInvalid("h'ost'") - self.assertInvalid("h'ost") - self.assertInvalid("h$ost") - self.assertInvalid("h%ost") - self.assertInvalid("host_01.co.uk") - self.assertInvalid("host;name=99") - self.assertInvalid('___site0.1001') - self.assertInvalid('_site01001') - self.assertInvalid("host..name") - self.assertInvalid(".host.name.com") - self.assertInvalid("no spaces") - - def test_no_start_end_hyphens(self): - self.assertInvalid("-host.com") - self.assertInvalid("-hostname.com-") - self.assertInvalid("hostname.co.uk-") - - def test_strip_trailing_dot(self): - self.assertConvertedValue('cell1.nova.site1.', 'cell1.nova.site1') - self.assertConvertedValue('cell1.', 'cell1') - - def test_valid_hostname(self): - self.assertConvertedEqual('cell1.nova.site1') - self.assertConvertedEqual('site01001') - self.assertConvertedEqual('home-site-here.org.com') - self.assertConvertedEqual('192.168.0.1') - self.assertConvertedEqual('1.1.1') - self.assertConvertedEqual('localhost') - - def test_max_segment_size(self): - self.assertConvertedEqual('host.%s.com' % ('x' * 63)) - self.assertInvalid('host.%s.com' % ('x' * 64)) - - def test_max_hostname_size(self): - test_str = '.'.join('x'*31 for x in range(8)) - self.assertEqual(255, len(test_str)) - self.assertInvalid(test_str) - self.assertConvertedEqual(test_str[:-2]) - - -class URITypeTests(TypeTestHelper, unittest.TestCase): - type = types.URI() - - def test_uri(self): - self.assertConvertedValue('http://example.com', 'http://example.com') - self.assertInvalid('invalid') # it doesn't include a scheme - self.assertInvalid('http://') # it doesn't include an authority - - def test_repr(self): - self.assertEqual('URI', repr(types.URI())) - - def test_max_length(self): - self.type_instance = types.String(max_length=30) - self.assertInvalid('http://www.example.com/versions') - self.assertConvertedValue('http://www.example.com', - 'http://www.example.com') - - -class PortTypeTests(TypeTestHelper, unittest.TestCase): - type = types.Port() - - def test_port(self): - self.assertInvalid(-1) - self.assertInvalid(65536) - self.assertConvertedValue('80', 80) - self.assertConvertedValue('65535', 65535) - - def test_repr(self): - self.assertEqual('Port(min=0, max=65535)', repr(types.Port())) - - def test_repr_with_min(self): - t = types.Port(min=123) - self.assertEqual('Port(min=123, max=65535)', repr(t)) - - def test_repr_with_max(self): - t = types.Port(max=456) - self.assertEqual('Port(min=0, max=456)', repr(t)) - - def test_repr_with_min_and_max(self): - t = types.Port(min=123, max=456) - self.assertEqual('Port(min=123, max=456)', repr(t)) - t = types.Port(min=0, max=0) - self.assertEqual('Port(min=0, max=0)', repr(t)) - - def test_repr_with_choices(self): - t = types.Port(choices=[80, 457]) - self.assertEqual('Port(choices=[80, 457])', repr(t)) - - def test_choices(self): - t = types.Port(choices=[80, 457]) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - t(80) - t(457) - - def test_invalid_choices(self): - self.assertRaises(ValueError, types.Port, choices=[-1, 457]) - self.assertRaises(ValueError, types.Port, choices=[1, 2, 3, 65536]) - - def test_equal(self): - self.assertTrue(types.Port() == types.Port()) - - def test_equal_with_same_min_and_no_max(self): - self.assertTrue(types.Port(min=123) == types.Port(min=123)) - - def test_equal_with_same_max_and_no_min(self): - self.assertTrue(types.Port(max=123) == types.Port(max=123)) - - def test_equal_with_same_min_and_max(self): - t1 = types.Port(min=1, max=123) - t2 = types.Port(min=1, max=123) - self.assertTrue(t1 == t2) - - def test_equal_with_same_choices(self): - t1 = types.Port(choices=[80, 457]) - t2 = types.Port(choices=[457, 80]) - self.assertTrue(t1 == t2) - - def test_not_equal(self): - self.assertFalse(types.Port(min=123) == types.Port(min=456)) - self.assertFalse(types.Port(choices=[80, 457]) == - types.Port(choices=[80, 40])) - self.assertFalse(types.Port(choices=[80, 457]) == - types.Port()) - - def test_not_equal_to_other_class(self): - self.assertFalse(types.Port() == types.Integer()) - - def test_choices_with_min_max(self): - self.assertRaises(ValueError, - types.Port, - min=100, - choices=[50, 60]) - self.assertRaises(ValueError, - types.Port, - max=10, - choices=[50, 60]) - types.Port(min=10, max=100, choices=[50, 60]) - - def test_min_greater_max(self): - self.assertRaises(ValueError, - types.Port, - min=100, max=50) - self.assertRaises(ValueError, - types.Port, - min=-50, max=-100) - self.assertRaises(ValueError, - types.Port, - min=0, max=-50) - self.assertRaises(ValueError, - types.Port, - min=50, max=0) - - def test_illegal_min(self): - self.assertRaises(ValueError, - types.Port, - min=-1, max=50) - self.assertRaises(ValueError, - types.Port, - min=-50) - - def test_illegal_max(self): - self.assertRaises(ValueError, - types.Port, - min=100, max=65537) - self.assertRaises(ValueError, - types.Port, - max=100000) - - def test_with_max_and_min(self): - t = types.Port(min=123, max=456) - self.assertRaises(ValueError, t, 122) - t(123) - t(300) - t(456) - self.assertRaises(ValueError, t, 0) - self.assertRaises(ValueError, t, 457) - - def test_with_min_zero(self): - t = types.Port(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.Port(max=0) - self.assertRaises(ValueError, t, 1) - t(0) - - def test_with_choices_list(self): - t = types.Port(choices=[80, 457]) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) - - def test_with_choices_tuple(self): - t = types.Port(choices=(80, 457)) - self.assertRaises(ValueError, t, 1) - self.assertRaises(ValueError, t, 200) - self.assertRaises(ValueError, t, -457) - t(80) - t(457) diff --git a/oslo_config/tests/testmods/__init__.py b/oslo_config/tests/testmods/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/oslo_config/tests/testmods/bar_foo_opt.py b/oslo_config/tests/testmods/bar_foo_opt.py deleted file mode 100644 index d670314..0000000 --- a/oslo_config/tests/testmods/bar_foo_opt.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -CONF = cfg.CONF - -CONF.register_opt(cfg.StrOpt('foo'), group='bar') diff --git a/oslo_config/tests/testmods/baz_qux_opt.py b/oslo_config/tests/testmods/baz_qux_opt.py deleted file mode 100644 index c764a66..0000000 --- a/oslo_config/tests/testmods/baz_qux_opt.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2013 Intel Corporation. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -CONF = cfg.CONF - -CONF.register_opt(cfg.StrOpt('baz'), group='qux') diff --git a/oslo_config/tests/testmods/blaa_opt.py b/oslo_config/tests/testmods/blaa_opt.py deleted file mode 100644 index 5c291db..0000000 --- a/oslo_config/tests/testmods/blaa_opt.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -CONF = cfg.CONF - -CONF.register_opt(cfg.StrOpt('blaa')) diff --git a/oslo_config/tests/testmods/fbaar_baa_opt.py b/oslo_config/tests/testmods/fbaar_baa_opt.py deleted file mode 100644 index 4e8e6ac..0000000 --- a/oslo_config/tests/testmods/fbaar_baa_opt.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -CONF = cfg.CONF - -opt = cfg.StrOpt('baa') - -CONF.register_opt(opt, group='fbaar') diff --git a/oslo_config/tests/testmods/fbar_foo_opt.py b/oslo_config/tests/testmods/fbar_foo_opt.py deleted file mode 100644 index 634959e..0000000 --- a/oslo_config/tests/testmods/fbar_foo_opt.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2013 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -CONF = cfg.CONF - -opt = cfg.StrOpt('foo') - -CONF.register_opt(opt, group='fbar') diff --git a/oslo_config/tests/testmods/fblaa_opt.py b/oslo_config/tests/testmods/fblaa_opt.py deleted file mode 100644 index ceaffbb..0000000 --- a/oslo_config/tests/testmods/fblaa_opt.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2012 Red Hat, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_config import cfg - -CONF = cfg.CONF - -opt = cfg.StrOpt('fblaa') - -CONF.register_opt(opt) diff --git a/oslo_config/types.py b/oslo_config/types.py deleted file mode 100644 index 07d5188..0000000 --- a/oslo_config/types.py +++ /dev/null @@ -1,738 +0,0 @@ -# Copyright 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Type conversion and validation classes for configuration options. - -Use these classes as values for the `type` argument to -:class:`oslo_config.cfg.Opt` and its subclasses. - -.. versionadded:: 1.3 -""" -import operator -import re -import warnings - -import abc -import netaddr -import rfc3986 -import six - - -@six.add_metaclass(abc.ABCMeta) -class ConfigType(object): - def __init__(self, type_name='unknown type'): - self.type_name = type_name - - NONE_DEFAULT = '' - - def format_defaults(self, default, sample_default=None): - """Return a list of formatted default values. - - """ - if sample_default is not None: - default_str = sample_default - elif default is None: - default_str = self.NONE_DEFAULT - else: - default_str = self._formatter(default) - return [default_str] - - def quote_trailing_and_leading_space(self, str_val): - if not isinstance(str_val, six.string_types): - warnings.warn('converting \'%s\' to a string' % str_val) - str_val = six.text_type(str_val) - if str_val.strip() != str_val: - return '"%s"' % str_val - return str_val - - @abc.abstractmethod - def _formatter(self, value): - pass - - -class String(ConfigType): - - """String type. - - String values do not get transformed and are returned as str objects. - - :param choices: Optional sequence of valid values. Mutually - exclusive with 'regex'. - :param quotes: If True and string is enclosed with single or double - quotes, will strip those quotes. Will signal error if - string have quote at the beginning and no quote at - the end. Turned off by default. Useful if used with - container types like List. - :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. - :param type_name: Type name to be used in the sample config file. - - .. versionchanged:: 2.1 - Added *regex* parameter. - - .. versionchanged:: 2.5 - Added *ignore_case* parameter. - - .. versionchanged:: 2.7 - Added *max_length* parameter. - Added *type_name* parameter. - """ - - def __init__(self, choices=None, quotes=False, regex=None, - ignore_case=False, max_length=None, - type_name='string value'): - super(String, self).__init__(type_name=type_name) - if choices and regex: - raise ValueError("'choices' and 'regex' cannot both be specified") - - self.ignore_case = ignore_case - self.quotes = quotes - 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) - if self.quotes and value: - if value[0] in "\"'": - if value[-1] != value[0]: - 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: - 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( - 'Valid values are [%s], but found %s' % ( - ', '.join([str(v) for v in self.choices]), - repr(value))) - - def __repr__(self): - details = [] - if self.choices: - details.append("choices=%r" % self.choices) - if self.regex: - details.append("regex=%r" % self.regex.pattern) - if details: - return "String(%s)" % ",".join(details) - return 'String' - - def __eq__(self, other): - return ( - (self.__class__ == other.__class__) and - (set(self.choices) == set(other.choices) if - self.choices and other.choices else - self.choices == other.choices) and - (self.quotes == other.quotes) and - (self.regex == other.regex) - ) - - def _formatter(self, value): - return self.quote_trailing_and_leading_space(value) - - -class MultiString(String): - """Multi-valued string.""" - - def __init__(self, type_name='multi valued'): - super(MultiString, self).__init__(type_name=type_name) - - NONE_DEFAULT = [''] - - def format_defaults(self, default, sample_default=None): - """Return a list of formatted default values. - - """ - if sample_default is not None: - default_list = self._formatter(sample_default) - elif not default: - default_list = self.NONE_DEFAULT - else: - default_list = self._formatter(default) - return default_list - - def _formatter(self, value): - return [self.quote_trailing_and_leading_space(v) for v in value] - - -class Boolean(ConfigType): - - """Boolean type. - - Values are case insensitive and can be set using - 1/0, yes/no, true/false or on/off. - - :param type_name: Type name to be used in the sample config file. - - .. versionchanged:: 2.7 - - Added *type_name* parameter. - """ - TRUE_VALUES = ['true', '1', 'on', 'yes'] - FALSE_VALUES = ['false', '0', 'off', 'no'] - - def __init__(self, type_name='boolean value'): - super(Boolean, self).__init__(type_name=type_name) - - def __call__(self, value): - if isinstance(value, bool): - return value - - s = value.lower() - if s in self.TRUE_VALUES: - return True - elif s in self.FALSE_VALUES: - return False - else: - raise ValueError('Unexpected boolean value %r' % value) - - def __repr__(self): - return 'Boolean' - - def __eq__(self, other): - return self.__class__ == other.__class__ - - def _formatter(self, value): - return str(value).lower() - - -class Number(ConfigType): - - """Number class, base for Integer and Float. - - :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. - :param type_name: Type name to be used in the sample config file. - :param choices: Optional sequence of valid values. - :param num_type: the type of number used for casting (i.e int, float) - - .. versionadded:: 3.14 - """ - - def __init__(self, num_type, type_name, - min=None, max=None, choices=None): - super(Number, self).__init__(type_name=type_name) - - if min is not None and max is not None and max < min: - raise ValueError('Max value is less than min value') - invalid_choices = [c for c in choices or [] - if (min is not None and min > c) - or (max is not None and max < c)] - if invalid_choices: - raise ValueError("Choices %s are out of bounds [%s..%s]" - % (invalid_choices, min, max)) - self.min = min - self.max = max - self.choices = choices - self.num_type = num_type - - def __call__(self, value): - if not isinstance(value, self.num_type): - s = str(value).strip() - if s == '': - return None - value = self.num_type(value) - - if self.choices is None: - if self.min is not None and value < self.min: - raise ValueError('Should be greater than or equal to %g' % - self.min) - if self.max is not None and value > self.max: - raise ValueError('Should be less than or equal to %g' % - self.max) - else: - if value not in self.choices: - raise ValueError('Valid values are %r, but found %g' % ( - self.choices, value)) - return value - - def __repr__(self): - props = [] - if self.choices is not None: - props.append("choices=%r" % (self.choices,)) - else: - if self.min is not None: - props.append('min=%g' % self.min) - if self.max is not None: - props.append('max=%g' % self.max) - - if props: - return self.__class__.__name__ + '(%s)' % ', '.join(props) - return self.__class__.__name__ - - def __eq__(self, other): - return ( - (self.__class__ == other.__class__) and - (self.min == other.min) and - (self.max == other.max) and - (set(self.choices) == set(other.choices) if - self.choices and other.choices else - self.choices == other.choices) - ) - - def _formatter(self, value): - return str(value) - - -class Integer(Number): - - """Integer type. - - Converts value to an integer optionally doing range checking. - If value is whitespace or empty string will return None. - - :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. - :param type_name: Type name to be used in the sample config file. - :param choices: Optional sequence of valid values. - - .. versionchanged:: 2.4 - The class now honors zero for *min* and *max* parameters. - - .. versionchanged:: 2.7 - Added *type_name* parameter. - - .. versionchanged:: 3.2 - Added *choices* parameter. - - .. versionchanged:: 3.16 - *choices* is no longer mutually exclusive with *min*/*max*. If those are - supplied, all choices are verified to be within the range. - """ - - def __init__(self, min=None, max=None, type_name='integer value', - choices=None): - super(Integer, self).__init__(int, type_name, min=min, max=max, - choices=choices) - - -class Float(Number): - - """Float type. - - :param type_name: Type name to be used in the sample config file. - :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. - - .. versionchanged:: 2.7 - - Added *type_name* parameter. - - .. versionchanged:: 3.14 - - Added *min* and *max* parameters. If *choices* are also supplied, they - must be within the range. - """ - - def __init__(self, min=None, max=None, type_name='floating point value'): - super(Float, self).__init__(float, type_name, min=min, max=max) - - -class Port(Integer): - - """Port type - - Represents a L4 Port. - - :param type_name: Type name to be used in the sample config file. - :param choices: Optional sequence of valid values. - :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. - - .. versionadded:: 3.16 - """ - - PORT_MIN = 0 - PORT_MAX = 65535 - - def __init__(self, min=None, max=None, type_name='port', choices=None): - min = self.PORT_MIN if min is None else min - max = self.PORT_MAX if max is None else max - if min < self.PORT_MIN: - raise ValueError('Min value cannot be less than %(min)d', - {'min': self.PORT_MIN}) - if max > self.PORT_MAX: - raise ValueError('Max value cannot be more than %(max)d', - {'max': self.PORT_MAX}) - - super(Port, self).__init__(min=min, max=max, type_name=type_name, - choices=choices) - - -class List(ConfigType): - - """List type. - - Represent values of other (item) type, separated by commas. - The resulting value is a list containing those values. - - List doesn't know if item type can also contain commas. To workaround this - it tries the following: if the next part fails item validation, it appends - comma and next item until validation succeeds or there is no parts left. - In the later case it will signal validation error. - - :param item_type: type of list items - :param bounds: if True, value should be inside "[" and "]" pair - :param type_name: Type name to be used in the sample config file. - - .. versionchanged:: 2.7 - - Added *type_name* parameter. - """ - - def __init__(self, item_type=None, bounds=False, type_name='list value'): - super(List, self).__init__(type_name=type_name) - - if item_type is None: - item_type = String() - - if not callable(item_type): - raise TypeError('item_type must be callable') - self.item_type = item_type - self.bounds = bounds - - def __call__(self, value): - if isinstance(value, list): - return value - - result = [] - s = value.strip() - - if self.bounds: - if not s.startswith('['): - raise ValueError('Value should start with "["') - if not s.endswith(']'): - raise ValueError('Value should end with "]"') - s = s[1:-1] - - if s == '': - return result - - values = s.split(',') - while values: - value = values.pop(0) - while True: - first_error = None - try: - validated_value = self.item_type(value.strip()) - break - except ValueError as e: - if not first_error: - first_error = e - if len(values) == 0: - raise first_error - - value += ',' + values.pop(0) - - result.append(validated_value) - - return result - - def __repr__(self): - return 'List of %s' % repr(self.item_type) - - def __eq__(self, other): - return ( - (self.__class__ == other.__class__) and - (self.item_type == other.item_type) - ) - - def _formatter(self, value): - if isinstance(value, six.string_types): - return value - return ','.join(value) - - -class Dict(ConfigType): - - """Dictionary type. - - Dictionary type values are key:value pairs separated by commas. - The resulting value is a dictionary of these key/value pairs. - Type of dictionary key is always string, but dictionary value - type can be customized. - - :param value_type: type of values in dictionary - :param bounds: if True, value should be inside "{" and "}" pair - :param type_name: Type name to be used in the sample config file. - - .. versionchanged:: 2.7 - - Added *type_name* parameter. - """ - - def __init__(self, value_type=None, bounds=False, type_name='dict value'): - super(Dict, self).__init__(type_name=type_name) - - if value_type is None: - value_type = String() - - if not callable(value_type): - raise TypeError('value_type must be callable') - self.value_type = value_type - self.bounds = bounds - - def __call__(self, value): - if isinstance(value, dict): - return value - - result = {} - s = value.strip() - - if self.bounds: - if not s.startswith('{'): - raise ValueError('Value should start with "{"') - if not s.endswith('}'): - raise ValueError('Value should end with "}"') - s = s[1:-1] - - if s == '': - return result - - pairs = s.split(',') - while pairs: - pair = pairs.pop(0) - - while True: - first_error = None - try: - key_value = pair.split(':', 1) - - if len(key_value) < 2: - raise ValueError('Value should be NAME:VALUE pairs ' - 'separated by ","') - - key, value = key_value - key = key.strip() - value = value.strip() - - value = self.value_type(value) - break - except ValueError as e: - if not first_error: - first_error = e - if not pairs: - raise first_error - - pair += ',' + pairs.pop(0) - - if key == '': - raise ValueError('Key name should not be empty') - - if key in result: - raise ValueError('Duplicate key %s' % key) - - result[key] = value - - return result - - def __repr__(self): - return 'Dict of %s' % repr(self.value_type) - - def __eq__(self, other): - return ( - (self.__class__ == other.__class__) and - (self.value_type == other.value_type) - ) - - def _formatter(self, value): - sorted_items = sorted(value.items(), - key=operator.itemgetter(0)) - return ','.join(['%s:%s' % i for i in sorted_items]) - - -class IPAddress(ConfigType): - - """IP address type - - Represents either ipv4 or ipv6. Without specifying version parameter both - versions are checked - - :param version: defines which version should be explicitly checked (4 or 6) - :param type_name: Type name to be used in the sample config file. - - .. versionchanged:: 2.7 - - Added *type_name* parameter. - """ - - def __init__(self, version=None, type_name='IP address value'): - super(IPAddress, self).__init__(type_name=type_name) - version_checkers = { - None: self._check_both_versions, - 4: self._check_ipv4, - 6: self._check_ipv6 - } - - self.version_checker = version_checkers.get(version) - if self.version_checker is None: - raise TypeError("%s is not a valid IP version." % version) - - def __call__(self, value): - value = str(value) - if not value: - raise ValueError("IP address cannot be an empty string") - self.version_checker(value) - return value - - def __repr__(self): - return "IPAddress" - - def __eq__(self, other): - return self.__class__ == other.__class__ - - def _check_ipv4(self, address): - if not netaddr.valid_ipv4(address, netaddr.core.INET_PTON): - raise ValueError("%s is not an IPv4 address" % address) - - def _check_ipv6(self, address): - if not netaddr.valid_ipv6(address, netaddr.core.INET_PTON): - raise ValueError("%s is not an IPv6 address" % address) - - def _check_both_versions(self, address): - if not (netaddr.valid_ipv4(address, netaddr.core.INET_PTON) or - netaddr.valid_ipv6(address, netaddr.core.INET_PTON)): - raise ValueError("%s is not IPv4 or IPv6 address" % address) - - def _formatter(self, value): - return value - - -class Hostname(ConfigType): - """Hostname type. - - A hostname refers to a valid DNS or hostname. It must not be longer than - 253 characters, have a segment greater than 63 characters, nor start or - end with a hyphen. - - :param type_name: Type name to be used in the sample config file. - - """ - - def __init__(self, type_name='hostname value'): - super(Hostname, self).__init__(type_name=type_name) - - def __call__(self, value): - """Check hostname is valid. - - Ensures that each segment - - Contains at least one character and a maximum of 63 characters - - Consists only of allowed characters: letters (A-Z and a-z), - digits (0-9), and hyphen (-) - - Does not begin or end with a hyphen - - maximum total length of 253 characters - - For more details , please see: http://tools.ietf.org/html/rfc1035 - """ - - if len(value) == 0: - raise ValueError("Cannot have an empty hostname") - if len(value) > 253: - raise ValueError("hostname is greater than 253 characters: %s" - % value) - if value.endswith("."): - value = value[:-1] - allowed = re.compile("(?!-)[A-Z0-9-]{1,63}(? self.max_length: - raise ValueError("Value '%s' exceeds maximum length %d" % - (value, self.max_length)) - self.value = value - return value - - def __repr__(self): - return 'URI' - - def __eq__(self, other): - return ( - (self.__class__ == other.__class__) and - (self.value == other.value) - ) - - def _formatter(self, value): - return value diff --git a/oslo_config/version.py b/oslo_config/version.py deleted file mode 100644 index 14f0590..0000000 --- a/oslo_config/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2016 OpenStack Foundation -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - - -import pbr.version - -version_info = pbr.version.VersionInfo('oslo_config') diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/notes/add-float-min-max-b1a2e16301c8435c.yaml b/releasenotes/notes/add-float-min-max-b1a2e16301c8435c.yaml deleted file mode 100644 index dffb4ad..0000000 --- a/releasenotes/notes/add-float-min-max-b1a2e16301c8435c.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -features: - - Added minimum and maximum value limits to FloatOpt. \ No newline at end of file diff --git a/releasenotes/notes/add-port_type-8704295c6a56265d.yaml b/releasenotes/notes/add-port_type-8704295c6a56265d.yaml deleted file mode 100644 index 9896164..0000000 --- a/releasenotes/notes/add-port_type-8704295c6a56265d.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - Integer and Float now support *min*, *max* and *choices*. Choices must - respect *min* and *max* (if provided). - - Added Port type as an Integer in the closed interval [0, 65535]. diff --git a/releasenotes/notes/add-reno-71dc832ce29b962f.yaml b/releasenotes/notes/add-reno-71dc832ce29b962f.yaml deleted file mode 100644 index 8cf5238..0000000 --- a/releasenotes/notes/add-reno-71dc832ce29b962f.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -other: - - Start using reno for managing release notes. \ No newline at end of file diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py deleted file mode 100644 index ca82205..0000000 --- a/releasenotes/source/conf.py +++ /dev/null @@ -1,277 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# oslo.config Release Notes documentation build configuration file, created by -# sphinx-quickstart on Tue Nov 3 17:40:50 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'oslosphinx', - 'reno.sphinxext', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'oslo.config Release Notes' -copyright = u'2016, oslo.config Developers' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -from oslo_config.version import version_info as oslo_config_version -# The full version, including alpha/beta/rc tags. -release = oslo_config_version.version_string_with_vcs() -# The short X.Y version. -version = oslo_config_version.canonical_version_string() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'oslo.configReleaseNotesdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # 'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'oslo.configReleaseNotes.tex', - u'oslo.config Release Notes Documentation', - u'oslo.config Developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'oslo.configreleasenotes', - u'oslo.config Release Notes Documentation', - [u'oslo.config Developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'oslo.configReleaseNotes', - u'oslo.config Release Notes Documentation', - u'oslo.config Developers', 'oslo.configReleaseNotes', - 'An OpenStack library for parsing configuration options from the command' - ' line and configuration files.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst deleted file mode 100644 index efa4639..0000000 --- a/releasenotes/source/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -=========================== - oslo.config Release Notes -=========================== - - .. toctree:: - :maxdepth: 1 - - unreleased - liberty - mitaka diff --git a/releasenotes/source/liberty.rst b/releasenotes/source/liberty.rst deleted file mode 100644 index 67b4cd9..0000000 --- a/releasenotes/source/liberty.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================ -Liberty Series Release Notes -============================ - -.. release-notes:: - :branch: origin/stable/liberty diff --git a/releasenotes/source/mitaka.rst b/releasenotes/source/mitaka.rst deleted file mode 100644 index e545609..0000000 --- a/releasenotes/source/mitaka.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Mitaka Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/mitaka diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst deleted file mode 100644 index 5860a46..0000000 --- a/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -========================== - Unreleased Release Notes -========================== - -.. release-notes:: diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 972e955..0000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -debtcollector>=1.2.0 # Apache-2.0 -netaddr!=0.7.16,>=0.7.12 # BSD -six>=1.9.0 # MIT -stevedore>=1.16.0 # Apache-2.0 -oslo.i18n>=2.1.0 # Apache-2.0 -rfc3986>=0.2.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 72a3568..0000000 --- a/setup.cfg +++ /dev/null @@ -1,49 +0,0 @@ -[metadata] -name = oslo.config -author = OpenStack -author-email = openstack-dev@lists.openstack.org -summary = Oslo Configuration API -description-file = - README.rst -home-page = https://wiki.openstack.org/wiki/Oslo#oslo.config -classifier = - Development Status :: 5 - Production/Stable - Environment :: OpenStack - Intended Audience :: Developers - Intended Audience :: Information Technology - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: 2 - Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 - -[files] -packages = - oslo_config - -[global] -setup-hooks = - pbr.hooks.setup_hook - -[entry_points] -console_scripts = - oslo-config-generator = oslo_config.generator:main -oslo.config.opts = - oslo.config = oslo_config._list_opts:list_opts - -[build_sphinx] -source-dir = doc/source -build-dir = doc/build -all_files = 1 - -[upload_sphinx] -upload-dir = doc/build/html - -[pbr] -warnerrors = True - -[wheel] -universal = 1 diff --git a/setup.py b/setup.py deleted file mode 100644 index 782bb21..0000000 --- a/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT -import setuptools - -# In python < 2.7.4, a lazy loading of package `pbr` will break -# setuptools if some other modules registered functions in `atexit`. -# solution from: http://bugs.python.org/issue15881#msg170215 -try: - import multiprocessing # noqa -except ImportError: - pass - -setuptools.setup( - setup_requires=['pbr>=1.8'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index a11d8f2..0000000 --- a/test-requirements.txt +++ /dev/null @@ -1,31 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. - -hacking<0.11,>=0.10.0 - -fixtures>=3.0.0 # Apache-2.0/BSD -python-subunit>=0.0.18 # Apache-2.0/BSD -testrepository>=0.0.18 # Apache-2.0/BSD -testscenarios>=0.4 # Apache-2.0/BSD -testtools>=1.4.0 # MIT -oslotest>=1.10.0 # Apache-2.0 - -# when we can require tox>= 1.4, this can go into tox.ini: -# [testenv:cover] -# deps = {[testenv]deps} coverage -coverage>=3.6 # Apache-2.0 - -# this is required for the docs build jobs -sphinx!=1.3b1,<1.3,>=1.2.1 # BSD -oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 -reno>=1.8.0 # Apache2 - -# Required only for tests -oslo.i18n>=2.1.0 # Apache-2.0 - -# mocking framework -mock>=2.0 # BSD - -# Bandit security code scanner -bandit>=1.0.1 # Apache-2.0 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 463fac2..0000000 --- a/tox.ini +++ /dev/null @@ -1,49 +0,0 @@ -[tox] -distribute = False -envlist = py35,py34,py27,pep8 - -[testenv] -deps = -r{toxinidir}/test-requirements.txt -commands = - python setup.py test --coverage --coverage-package-name=oslo_config --slowest --testr-args='{posargs}' - coverage report --show-missing - -[testenv:pep8] -commands = - flake8 - # Run security linter - bandit -r oslo_config -x tests -n5 - -[testenv:cover] -setenv = VIRTUAL_ENV={envdir} -commands = - python setup.py test --coverage --coverage-package-name=oslo_config --testr-args='{posargs}' - coverage report --show-missing - -[testenv:venv] -commands = {posargs} - -[testenv:docs] -commands = python setup.py build_sphinx - -[testenv:bandit] -deps = -r{toxinidir}/test-requirements.txt -commands = bandit -r oslo_config -x tests -n5 - -[flake8] -show-source = True -exclude = .tox,dist,doc,*.egg,build - -[testenv:pip-missing-reqs] -# do not install test-requirements as that will pollute the virtualenv for -# determining missing packages -# this also means that pip-missing-reqs must be installed separately, outside -# of the requirements.txt files -deps = pip_missing_reqs -commands = pip-missing-reqs -d --ignore-module=oslo_config* --ignore-module=pkg_resources --ignore-file=oslo_config/tests/* --ignore-file=tests/ oslo_config - -[testenv:releasenotes] -commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html - -[hacking] -import_exceptions = oslo_config._i18n