diff options
-rw-r--r-- | doc/source/cli/property-keys.rst | 11 | ||||
-rw-r--r-- | doc/source/conf.py | 8 | ||||
-rw-r--r-- | glanceclient/common/utils.py | 25 | ||||
-rw-r--r-- | glanceclient/tests/functional/v1/test_readonly_glance.py | 9 | ||||
-rw-r--r-- | glanceclient/tests/functional/v2/test_readonly_glance.py | 9 | ||||
-rw-r--r-- | glanceclient/tests/unit/test_http.py | 24 | ||||
-rw-r--r-- | glanceclient/tests/unit/test_utils.py | 16 | ||||
-rw-r--r-- | glanceclient/tests/unit/v1/test_images.py | 4 | ||||
-rw-r--r-- | glanceclient/tests/unit/v2/test_images.py | 8 | ||||
-rw-r--r-- | glanceclient/tests/unit/v2/test_shell_v2.py | 2 | ||||
-rw-r--r-- | releasenotes/notes/boolean-properties-strict-checking-bdd624b5da81e723.yaml | 12 | ||||
-rw-r--r-- | releasenotes/source/conf.py | 16 | ||||
-rw-r--r-- | tox.ini | 4 |
13 files changed, 80 insertions, 68 deletions
diff --git a/doc/source/cli/property-keys.rst b/doc/source/cli/property-keys.rst index 9e59124..7176779 100644 --- a/doc/source/cli/property-keys.rst +++ b/doc/source/cli/property-keys.rst @@ -7,14 +7,14 @@ that can be consumed by other services to affect the behavior of those other services. Properties can be set on an image at the time of image creation or they -can be set on an existing image. Use the :command:`openstack image create` -and :command:`openstack image set` commands respectively. +can be set on an existing image. Use the :command:`glance image-create` +and :command:`glance image-update` commands respectively. For example: .. code-block:: console - $ openstack image set IMG-UUID --property architecture=x86_64 + $ glance image-update IMG-UUID --property architecture=x86_64 For a list of image properties that can be used to affect the behavior of other services, refer to `Useful image properties @@ -27,3 +27,8 @@ in the Glance Administration Guide. For more information, refer to `Manage images <https://docs.openstack.org/glance/latest/admin/manage-images.html>`_ in the Glance Administration Guide. + +.. note:: + + Boolean properties expect one of the following values: '0', '1', 'f', + 'false', 'n', 'no', 'off', 'on', 't', 'true', 'y', 'yes' (case-insensitive). diff --git a/doc/source/conf.py b/doc/source/conf.py index bfe9b56..c2a4066 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -57,7 +57,7 @@ master_doc = 'index' # General information about the project. project = 'python-glanceclient' -copyright = u'OpenStack Foundation' +copyright = 'OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True @@ -83,8 +83,8 @@ htmlhelp_basename = '%sdoc' % project # -- Options for man page output ---------------------------------------------- # Grouping the document tree for man pages. -# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' +# List of tuples 'sourcefile', 'target', 'title', 'Authors name', 'manual' man_pages = [ - ('cli/glance', 'glance', u'Client for OpenStack Images API', - [u'OpenStack Foundation'], 1), + ('cli/glance', 'glance', 'Client for OpenStack Images API', + ['OpenStack Foundation'], 1), ] diff --git a/glanceclient/common/utils.py b/glanceclient/common/utils.py index e131bd9..ff1ffd1 100644 --- a/glanceclient/common/utils.py +++ b/glanceclient/common/utils.py @@ -103,7 +103,7 @@ def schema_args(schema_getter, omit=None): typemap = { 'string': encodeutils.safe_decode, 'integer': int, - 'boolean': strutils.bool_from_string, + 'boolean': lambda x: strutils.bool_from_string(x, strict=True), 'array': list } @@ -271,34 +271,11 @@ def print_list(objs, fields, formatters=None, field_settings=None): field_name = field.lower().replace(' ', '_') data = getattr(o, field_name, None) or '' row.append(data) - count = 0 - # Converts unicode values in list to string - for part in row: - count = count + 1 - if isinstance(part, list): - part = unicode_key_value_to_string(part) - row[count - 1] = part pt.add_row(row) print(encodeutils.safe_decode(pt.get_string())) -def _encode(src): - """remove extra 'u' in PY2.""" - return src - - -def unicode_key_value_to_string(src): - """Recursively converts dictionary keys to strings.""" - if isinstance(src, dict): - return dict((_encode(k), - _encode(unicode_key_value_to_string(v))) - for k, v in src.items()) - if isinstance(src, list): - return [unicode_key_value_to_string(l) for l in src] - return _encode(src) - - def print_dict(d, max_column_width=80): pt = prettytable.PrettyTable(['Property', 'Value'], caching=False) pt.align = 'l' diff --git a/glanceclient/tests/functional/v1/test_readonly_glance.py b/glanceclient/tests/functional/v1/test_readonly_glance.py index a7024aa..ffd8358 100644 --- a/glanceclient/tests/functional/v1/test_readonly_glance.py +++ b/glanceclient/tests/functional/v1/test_readonly_glance.py @@ -51,7 +51,14 @@ class SimpleReadOnlyGlanceClientTest(base.ClientTestBase): commands = [] cmds_start = lines.index('Positional arguments:') - cmds_end = lines.index('Optional arguments:') + try: + # Starting in Python 3.10, argparse displays options in the + # "Options:" section... + cmds_end = lines.index('Options:') + except ValueError: + # ... but before Python 3.10, options were displayed in the + # "Optional arguments:" section. + cmds_end = lines.index('Optional arguments:') command_pattern = re.compile(r'^ {4}([a-z0-9\-\_]+)') for line in lines[cmds_start:cmds_end]: match = command_pattern.match(line) diff --git a/glanceclient/tests/functional/v2/test_readonly_glance.py b/glanceclient/tests/functional/v2/test_readonly_glance.py index 4d7f92d..7bc86d1 100644 --- a/glanceclient/tests/functional/v2/test_readonly_glance.py +++ b/glanceclient/tests/functional/v2/test_readonly_glance.py @@ -71,7 +71,14 @@ class SimpleReadOnlyGlanceClientTest(base.ClientTestBase): commands = [] cmds_start = lines.index('Positional arguments:') - cmds_end = lines.index('Optional arguments:') + try: + # Starting in Python 3.10, argparse displays options in the + # "Options:" section... + cmds_end = lines.index('Options:') + except ValueError: + # ... but before Python 3.10, options were displayed in the + # "Optional arguments:" section. + cmds_end = lines.index('Optional arguments:') command_pattern = re.compile(r'^ {4}([a-z0-9\-\_]+)') for line in lines[cmds_start:cmds_end]: match = command_pattern.match(line) diff --git a/glanceclient/tests/unit/test_http.py b/glanceclient/tests/unit/test_http.py index 5759ccd..fef3b6a 100644 --- a/glanceclient/tests/unit/test_http.py +++ b/glanceclient/tests/unit/test_http.py @@ -66,7 +66,7 @@ class TestClient(testtools.TestCase): self.endpoint = 'http://example.com:9292' self.ssl_endpoint = 'https://example.com:9292' - self.token = u'abc123' + self.token = 'abc123' self.client = getattr(self, self.create_client)() @@ -80,7 +80,7 @@ class TestClient(testtools.TestCase): 'X-Service-Catalog': 'service_catalog', } # with token - kwargs = {'token': u'fake-token', + kwargs = {'token': 'fake-token', 'identity_headers': identity_headers} http_client_object = http.HTTPClient(self.endpoint, **kwargs) self.assertEqual('auth_token', http_client_object.auth_token) @@ -96,10 +96,10 @@ class TestClient(testtools.TestCase): 'X-Service-Catalog': 'service_catalog', } # without X-Auth-Token in identity headers - kwargs = {'token': u'fake-token', + kwargs = {'token': 'fake-token', 'identity_headers': identity_headers} http_client_object = http.HTTPClient(self.endpoint, **kwargs) - self.assertEqual(u'fake-token', http_client_object.auth_token) + self.assertEqual('fake-token', http_client_object.auth_token) self.assertTrue(http_client_object.identity_headers. get('X-Auth-Token') is None) @@ -210,12 +210,12 @@ class TestClient(testtools.TestCase): self.mock.get(self.endpoint + path, text=text, headers={"Content-Type": "text/plain"}) - headers = {"test": u'ni\xf1o'} + headers = {"test": 'ni\xf1o'} resp, body = self.client.get(path, headers=headers) self.assertEqual(text, resp.text) def test_headers_encoding(self): - value = u'ni\xf1o' + value = 'ni\xf1o' fake_location = b'http://web_server:80/images/fake.img' headers = {"test": value, "none-val": None, @@ -262,7 +262,7 @@ class TestClient(testtools.TestCase): ksarqh = mock_ksarq.call_args[1]['headers'] # Only one Content-Type header (of any text-type) self.assertEqual(1, [encodeutils.safe_decode(key) - for key in ksarqh.keys()].count(u'Content-Type')) + for key in ksarqh.keys()].count('Content-Type')) # And it's the one we set self.assertEqual(b"application/openstack-images-v2.1-json-patch", ksarqh[b"Content-Type"]) @@ -295,7 +295,7 @@ class TestClient(testtools.TestCase): def test_parse_endpoint(self): endpoint = 'http://example.com:9292' - test_client = http.HTTPClient(endpoint, token=u'adc123') + test_client = http.HTTPClient(endpoint, token='adc123') actual = test_client.parse_endpoint(endpoint) expected = parse.SplitResult(scheme='http', netloc='example.com:9292', path='', @@ -304,7 +304,7 @@ class TestClient(testtools.TestCase): def test_get_connections_kwargs_http(self): endpoint = 'http://example.com:9292' - test_client = http.HTTPClient(endpoint, token=u'adc123') + test_client = http.HTTPClient(endpoint, token='adc123') self.assertEqual(600.0, test_client.timeout) def test__chunk_body_exact_size_chunk(self): @@ -321,7 +321,7 @@ class TestClient(testtools.TestCase): path = '/v1/images/' self.mock.post(self.endpoint + path, text=text) - headers = {"test": u'chunked_request'} + headers = {"test": 'chunked_request'} resp, body = self.client.post(path, headers=headers, data=data) self.assertIsInstance(self.mock.last_request.body, types.GeneratorType) self.assertEqual(text, resp.text) @@ -332,7 +332,7 @@ class TestClient(testtools.TestCase): text = 'OK' self.mock.post(self.endpoint + path, text=text) - headers = {"test": u'chunked_request'} + headers = {"test": 'chunked_request'} resp, body = self.client.post(path, headers=headers, data=data) self.assertEqual(text, resp.text) @@ -487,7 +487,7 @@ class TestClient(testtools.TestCase): headers = self.mock.last_request.headers self.assertEqual(refreshed_token, headers['X-Auth-Token']) # regression check for bug 1448080 - unicode_token = u'ni\xf1o+==' + unicode_token = 'ni\xf1o+==' http_client.auth_token = unicode_token http_client.get(path) headers = self.mock.last_request.headers diff --git a/glanceclient/tests/unit/test_utils.py b/glanceclient/tests/unit/test_utils.py index db08a1c..dfa7a44 100644 --- a/glanceclient/tests/unit/test_utils.py +++ b/glanceclient/tests/unit/test_utils.py @@ -122,7 +122,7 @@ class TestUtils(testtools.TestCase): # test for removing 'u' from lists in print_list output columns = ['ID', 'Tags'] images = [Struct(**{'id': 'b8e1c57e-907a-4239-aed8-0df8e54b8d2d', - 'tags': [u'Name1', u'Tag_123', u'veeeery long']})] + 'tags': ['Name1', 'Tag_123', 'veeeery long']})] saved_stdout = sys.stdout try: sys.stdout = output_list = io.StringIO() @@ -178,12 +178,6 @@ class TestUtils(testtools.TestCase): ''', output_list.getvalue()) - def test_unicode_key_value_to_string(self): - src = {u'key': u'\u70fd\u7231\u5a77'} - # u'xxxx' in PY3 is str, we will not get extra 'u' from cli - # output in PY3 - self.assertEqual(src, utils.unicode_key_value_to_string(src)) - def test_schema_args_with_list_types(self): # NOTE(flaper87): Regression for bug # https://bugs.launchpad.net/python-glanceclient/+bug/1401032 @@ -236,6 +230,14 @@ class TestUtils(testtools.TestCase): self.assertEqual(encodeutils.safe_decode, opts['type']) self.assertIn('None, opt-1, opt-2', opts['help']) + # Make sure we use strict checking for boolean values. + decorated = utils.schema_args(schema_getter('boolean'))(dummy_func) + arg, opts = decorated.__dict__['arguments'][0] + type_function = opts['type'] + self.assertEqual(type_function('False'), False) + self.assertEqual(type_function('True'), True) + self.assertRaises(ValueError, type_function, 'foo') + def test_iterable_closes(self): # Regression test for bug 1461678. def _iterate(i): diff --git a/glanceclient/tests/unit/v1/test_images.py b/glanceclient/tests/unit/v1/test_images.py index ef8256c..61c3d91 100644 --- a/glanceclient/tests/unit/v1/test_images.py +++ b/glanceclient/tests/unit/v1/test_images.py @@ -507,7 +507,7 @@ class ImageManagerTest(testtools.TestCase): self.assertEqual(False, image.is_public) self.assertEqual(False, image.protected) self.assertEqual(False, image.deleted) - self.assertEqual({u'arch': u'x86_64'}, image.properties) + self.assertEqual({'arch': 'x86_64'}, image.properties) def test_get_int(self): image = self.mgr.get(1) @@ -518,7 +518,7 @@ class ImageManagerTest(testtools.TestCase): self.assertEqual(False, image.is_public) self.assertEqual(False, image.protected) self.assertEqual(False, image.deleted) - self.assertEqual({u'arch': u'x86_64'}, image.properties) + self.assertEqual({'arch': 'x86_64'}, image.properties) def test_get_encoding(self): image = self.mgr.get('3') diff --git a/glanceclient/tests/unit/v2/test_images.py b/glanceclient/tests/unit/v2/test_images.py index 60ddb3a..7251203 100644 --- a/glanceclient/tests/unit/v2/test_images.py +++ b/glanceclient/tests/unit/v2/test_images.py @@ -550,10 +550,10 @@ data_fixtures = { { 'id': 'a2b83adc-888e-11e3-8872-78acc0b951d8', 'name': 'image-location-tests', - 'locations': [{u'url': u'http://foo.com/', - u'metadata': {u'foo': u'foometa'}}, - {u'url': u'http://bar.com/', - u'metadata': {u'bar': u'barmeta'}}], + 'locations': [{'url': 'http://foo.com/', + 'metadata': {'foo': 'foometa'}}, + {'url': 'http://bar.com/', + 'metadata': {'bar': 'barmeta'}}], }, ), 'PATCH': ( diff --git a/glanceclient/tests/unit/v2/test_shell_v2.py b/glanceclient/tests/unit/v2/test_shell_v2.py index b9ced58..8acceae 100644 --- a/glanceclient/tests/unit/v2/test_shell_v2.py +++ b/glanceclient/tests/unit/v2/test_shell_v2.py @@ -854,7 +854,7 @@ class ShellV2Test(testtools.TestCase): @mock.patch('sys.stdin', autospec=True) def test_do_image_create_with_unicode(self, mock_stdin): - name = u'\u041f\u0420\u0418\u0412\u0415\u0422\u0418\u041a' + name = '\u041f\u0420\u0418\u0412\u0415\u0422\u0418\u041a' args = self._make_args({'name': name, 'file': None}) diff --git a/releasenotes/notes/boolean-properties-strict-checking-bdd624b5da81e723.yaml b/releasenotes/notes/boolean-properties-strict-checking-bdd624b5da81e723.yaml new file mode 100644 index 0000000..bd0836d --- /dev/null +++ b/releasenotes/notes/boolean-properties-strict-checking-bdd624b5da81e723.yaml @@ -0,0 +1,12 @@ +--- +prelude: > +fixes: + - | + * Bug 1607317_: metadata def namespace update CLI is not working as expected for parameter "protected" + + .. _1607317: https://code.launchpad.net/bugs/1607317 +other: + - | + Boolean arguments now expect one of the following values: '0', '1', 'f', + 'false', 'n', 'no', 'off', 'on', 't', 'true', 'y', 'yes' + (case-insensitive). This will not change anything for most users. diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py index d20e2f4..907b784 100644 --- a/releasenotes/source/conf.py +++ b/releasenotes/source/conf.py @@ -55,8 +55,8 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'glanceclient Release Notes' -copyright = u'2016, Glance Developers' +project = 'glanceclient Release Notes' +copyright = '2016, Glance Developers' openstackdocs_repo_name = 'openstack/python-glanceclient' openstackdocs_bug_project = 'python-glanceclient' @@ -206,8 +206,8 @@ latex_elements = { # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'glanceclientReleaseNotes.tex', - u'glanceclient Release Notes Documentation', - u'Glance Developers', 'manual'), + 'glanceclient Release Notes Documentation', + 'Glance Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -237,8 +237,8 @@ latex_documents = [ # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'glanceclientreleasenotes', - u'glanceclient Release Notes Documentation', - [u'Glance Developers'], 1) + 'glanceclient Release Notes Documentation', + ['Glance Developers'], 1) ] # If true, show URL addresses after external links. @@ -252,8 +252,8 @@ man_pages = [ # dir menu entry, description, category) texinfo_documents = [ ('index', 'glanceclientReleaseNotes', - u'glanceclient Release Notes Documentation', - u'Glance Developers', 'glanceclientReleaseNotes', + 'glanceclient Release Notes Documentation', + 'Glance Developers', 'glanceclientReleaseNotes', 'Python bindings for the OpenStack Image service.', 'Miscellaneous'), ] @@ -54,7 +54,9 @@ commands = [testenv:docs] basepython = python3 -deps = -r{toxinidir}/doc/requirements.txt +deps = + -r{toxinidir}/requirements.txt + -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html sphinx-build -W -b man doc/source doc/build/man |