blob: ca350ed9a8556ded78a7426486e765ee1801d8d9 [file] [log] [blame]
Monty Taylorda3bada2012-11-22 09:38:22 -08001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2
3# Copyright 2012 OpenStack LLC
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17"""
18Utilities for consuming the auto-generated versioninfo files.
19"""
20
21import datetime
22import pkg_resources
23import os
24
25import setup
26
27
28class _deferred_version_string(object):
29 """Internal helper class which provides delayed version calculation."""
30 def __init__(self, version_info, prefix):
31 self.version_info = version_info
32 self.prefix = prefix
33
34 def __str__(self):
35 return "%s%s" % (self.prefix, self.version_info.version_string())
36
37 def __repr__(self):
38 return "%s%s" % (self.prefix, self.version_info.version_string())
39
40
41class VersionInfo(object):
42
43 def __init__(self, package, python_package=None, pre_version=None):
44 """Object that understands versioning for a package
45 :param package: name of the top level python namespace. For glance,
46 this would be "glance" for python-glanceclient, it
47 would be "glanceclient"
48 :param python_package: optional name of the project name. For
49 glance this can be left unset. For
50 python-glanceclient, this would be
51 "python-glanceclient"
52 :param pre_version: optional version that the project is working to
53 """
54 self.package = package
55 if python_package is None:
56 self.python_package = package
57 else:
58 self.python_package = python_package
59 self.pre_version = pre_version
60 self.version = None
61
62 def _generate_version(self):
63 """Defer to the openstack.common.setup routines for making a
64 version from git."""
65 if self.pre_version is None:
66 return setup.get_post_version(self.python_package)
67 else:
68 return setup.get_pre_version(self.python_package, self.pre_version)
69
70 def _newer_version(self, pending_version):
71 """Check to see if we're working with a stale version or not.
72 We expect a version string that either looks like:
73 2012.2~f3~20120708.10.4426392
74 which is an unreleased version of a pre-version, or:
75 0.1.1.4.gcc9e28a
76 which is an unreleased version of a post-version, or:
77 0.1.1
78 Which is a release and which should match tag.
79 For now, if we have a date-embedded version, check to see if it's
80 old, and if so re-generate. Otherwise, just deal with it.
81 """
82 try:
83 version_date = int(self.version.split("~")[-1].split('.')[0])
84 if version_date < int(datetime.date.today().strftime('%Y%m%d')):
85 return self._generate_version()
86 else:
87 return pending_version
88 except Exception:
89 return pending_version
90
91 def version_string_with_vcs(self, always=False):
92 """Return the full version of the package including suffixes indicating
93 VCS status.
94
95 For instance, if we are working towards the 2012.2 release,
96 canonical_version_string should return 2012.2 if this is a final
97 release, or else something like 2012.2~f1~20120705.20 if it's not.
98
99 :param always: if true, skip all version caching
100 """
101 if always:
102 self.version = self._generate_version()
103
104 if self.version is None:
105
106 requirement = pkg_resources.Requirement.parse(self.python_package)
107 versioninfo = "%s/versioninfo" % self.package
108 try:
109 raw_version = pkg_resources.resource_string(requirement,
110 versioninfo)
111 self.version = self._newer_version(raw_version.strip())
112 except (IOError, pkg_resources.DistributionNotFound):
113 self.version = self._generate_version()
114
115 return self.version
116
117 def canonical_version_string(self, always=False):
118 """Return the simple version of the package excluding any suffixes.
119
120 For instance, if we are working towards the 2012.2 release,
121 canonical_version_string should return 2012.2 in all cases.
122
123 :param always: if true, skip all version caching
124 """
125 return self.version_string_with_vcs(always).split('~')[0]
126
127 def version_string(self, always=False):
128 """Return the base version of the package.
129
130 For instance, if we are working towards the 2012.2 release,
131 version_string should return 2012.2 if this is a final release, or
132 2012.2-dev if it is not.
133
134 :param always: if true, skip all version caching
135 """
136 version_parts = self.version_string_with_vcs(always).split('~')
137 if len(version_parts) == 1:
138 return version_parts[0]
139 else:
140 return '%s-dev' % (version_parts[0],)
141
142 def deferred_version_string(self, prefix=""):
143 """Generate an object which will expand in a string context to
144 the results of version_string(). We do this so that don't
145 call into pkg_resources every time we start up a program when
146 passing version information into the CONF constructor, but
147 rather only do the calculation when and if a version is requested
148 """
149 return _deferred_version_string(self, prefix)