Browse Source

Fix version handling compatible with python-semanticversion 2.8

It seems the behavior of 'partial' argument of Version class __init__
has been changed between semantic-version 2.6.0 and 2.8.1
(though I could not identify the root cause).
'partial' argument is marked as deprecated in semantic-vesion 2.7.0,
so it is a good chance not to depend on 'partial' argument in horizon.

This commit uses Version.coerce() [1] instead to convert non-semver
version into a valid semver version.
We also need to keep the original version information as it is passed
when initializing python-*client (cinderclient and keystoneclient).

In addition, the previous implementation based on semantic-version 2.6.0
returns True for "Version("3.55") == 3". It depends on the behavior
of "partial" argument of semantic_version.Version. It was not
documented and it looks tricky to depend on this behavior.
"major" and "minor" properties are now introduced and api/cinder.py
is updated accordingly. I believe this approach is clearer and stable.

Unit test coverage on dict behavior is improved.
Variable names in the unit tests are adjusted to more meaningful ones.

[1] https://python-semanticversion.readthedocs.io/en/latest/#coercing-an-arbitrary-version-string

Change-Id: If0deee9d0289ff91d58d942b9612f7736356ae18
tags/16.0.0.0b2
Akihiro Motoki 2 weeks ago
parent
commit
5fd5b4c893

+ 28
- 6
openstack_dashboard/api/base.py View File

@@ -32,23 +32,45 @@ __all__ = ('APIResourceWrapper', 'APIDictWrapper',
32 32
 
33 33
 @functools.total_ordering
34 34
 class Version(object):
35
+    """A class to handle API version.
36
+
37
+    The current OpenStack APIs use the versioning of "<major>.<minor>",
38
+    so this class supports this style only.
39
+    """
40
+    # NOTE(amotoki): The implementation depends on the semantic_version library
41
+    # but we don't care the patch version in this class.
42
+
35 43
     def __init__(self, version):
36
-        self.version = semantic_version.Version(str(version), partial=True)
44
+        # NOTE(amotoki):
45
+        # All comparisons should use self.sem_ver as we would like to
46
+        # compare versions in the semantic versioning way.
47
+        # self.orig_ver should be used only in __str__ and __repr__
48
+        # to keep the original version information.
49
+        self.orig_ver = str(version)
50
+        self.sem_ver = semantic_version.Version.coerce(str(version))
51
+
52
+    @property
53
+    def major(self):
54
+        return self.sem_ver.major
55
+
56
+    @property
57
+    def minor(self):
58
+        return self.sem_ver.minor
37 59
 
38 60
     def __eq__(self, other):
39
-        return self.version == Version(other).version
61
+        return self.sem_ver == Version(other).sem_ver
40 62
 
41 63
     def __lt__(self, other):
42
-        return self.version < Version(other).version
64
+        return self.sem_ver < Version(other).sem_ver
43 65
 
44 66
     def __repr__(self):
45
-        return "Version('%s')" % self.version
67
+        return "Version('%s')" % self.orig_ver
46 68
 
47 69
     def __str__(self):
48
-        return str(self.version)
70
+        return str(self.orig_ver)
49 71
 
50 72
     def __hash__(self):
51
-        return hash(str(self.version))
73
+        return hash(str(self.sem_ver))
52 74
 
53 75
 
54 76
 class APIVersionManager(object):

+ 2
- 2
openstack_dashboard/api/cinder.py View File

@@ -233,9 +233,9 @@ def cinderclient(request, version=None):
233 233
     (username, token_id, tenant_id, cinder_urls,
234 234
         auth_url) = get_auth_params_from_request(request)
235 235
     version = base.Version(version)
236
-    if version == 2:
236
+    if version.major == 2:
237 237
         service_names = ('volumev2', 'volume')
238
-    elif version == 3:
238
+    elif version.major == 3:
239 239
         service_names = ('volumev3', 'volume')
240 240
     else:
241 241
         service_names = ('volume',)

+ 40
- 10
openstack_dashboard/test/unit/api/test_base.py View File

@@ -83,25 +83,55 @@ class APIVersionTests(test.TestCase):
83 83
         self.assertEqual(api_base.Version('1.0'), version)
84 84
 
85 85
     def test_greater(self):
86
-        version1 = api_base.Version('1.0')
86
+        version10 = api_base.Version('1.0')
87 87
         version12 = api_base.Version('1.2')
88 88
         version120 = api_base.Version('1.20')
89
-        self.assertGreater(version12, version1)
89
+        self.assertGreater(version12, version10)
90 90
         self.assertGreater(version120, version12)
91
-        self.assertEqual(version12, 1)  # sic!
92
-        self.assertGreater(1.2, version1)
91
+        self.assertGreater(version12, 1)
92
+        self.assertGreater(1.2, version10)
93 93
         self.assertGreater(version120, 1.2)
94 94
         self.assertGreater('1.20', version12)
95 95
 
96 96
     def test_dict(self):
97
-        version1 = api_base.Version('1.0')
98
-        version1b = api_base.Version('1.0')
99
-        self.assertIn(version1, {version1b: 1})
97
+        test_dict = {api_base.Version('1.0'): 1}
98
+
99
+        self.assertIn(api_base.Version('1'), test_dict)
100
+        self.assertIn(api_base.Version('1.0'), test_dict)
101
+        self.assertIn(api_base.Version('1.0.0'), test_dict)
102
+        self.assertIn(api_base.Version(1), test_dict)
103
+        self.assertIn(api_base.Version(1.0), test_dict)
104
+
105
+        self.assertNotIn(api_base.Version('1.2'), test_dict)
106
+        self.assertNotIn(api_base.Version('1.20'), test_dict)
107
+
108
+        test_dict = {api_base.Version('1.2'): 1}
109
+        self.assertIn(api_base.Version('1.2'), test_dict)
110
+        self.assertIn(api_base.Version('1.2.0'), test_dict)
111
+        self.assertIn(api_base.Version(1.2), test_dict)
112
+
113
+        self.assertNotIn(api_base.Version('1'), test_dict)
114
+        self.assertNotIn(api_base.Version('1.0'), test_dict)
115
+        self.assertNotIn(api_base.Version('1.0.0'), test_dict)
116
+        self.assertNotIn(api_base.Version(1), test_dict)
117
+        self.assertNotIn(api_base.Version(1.0), test_dict)
118
+        self.assertNotIn(api_base.Version('1.20'), test_dict)
100 119
 
101 120
     def test_text(self):
102
-        version1 = api_base.Version('1.0')
103
-        self.assertEqual("1.0", str(version1))
104
-        self.assertEqual("Version('1.0')", repr(version1))
121
+        version10 = api_base.Version('1.0')
122
+        self.assertEqual("1.0", str(version10))
123
+        self.assertEqual("Version('1.0')", repr(version10))
124
+
125
+    def test_major_minor(self):
126
+        version1 = api_base.Version('1')
127
+        version10 = api_base.Version('1.0')
128
+        version120 = api_base.Version('1.20')
129
+        self.assertEqual(1, version1.major)
130
+        self.assertEqual(0, version1.minor)
131
+        self.assertEqual(1, version10.major)
132
+        self.assertEqual(0, version10.minor)
133
+        self.assertEqual(1, version120.major)
134
+        self.assertEqual(20, version120.minor)
105 135
 
106 136
 
107 137
 # Wrapper classes that only define _attrs don't need extra testing.

Loading…
Cancel
Save