# vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2012 OpenStack LLC # # 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. """ Utilities for consuming the auto-generated versioninfo files. """ import datetime import pkg_resources import setup class _deferred_version_string(object): """Internal helper class which provides delayed version calculation.""" def __init__(self, version_info, prefix): self.version_info = version_info self.prefix = prefix def __str__(self): return "%s%s" % (self.prefix, self.version_info.version_string()) def __repr__(self): return "%s%s" % (self.prefix, self.version_info.version_string()) class VersionInfo(object): def __init__(self, package, python_package=None, pre_version=None): """Object that understands versioning for a package :param package: name of the top level python namespace. For glance, this would be "glance" for python-glanceclient, it would be "glanceclient" :param python_package: optional name of the project name. For glance this can be left unset. For python-glanceclient, this would be "python-glanceclient" :param pre_version: optional version that the project is working to """ self.package = package if python_package is None: self.python_package = package else: self.python_package = python_package self.pre_version = pre_version self.version = None def _generate_version(self): """Defer to the openstack.common.setup routines for making a version from git.""" if self.pre_version is None: return setup.get_post_version(self.python_package) else: return setup.get_pre_version(self.python_package, self.pre_version) def _newer_version(self, pending_version): """Check to see if we're working with a stale version or not. We expect a version string that either looks like: 2012.2~f3~20120708.10.4426392 which is an unreleased version of a pre-version, or: 0.1.1.4.gcc9e28a which is an unreleased version of a post-version, or: 0.1.1 Which is a release and which should match tag. For now, if we have a date-embedded version, check to see if it's old, and if so re-generate. Otherwise, just deal with it. """ try: version_date = int(self.version.split("~")[-1].split('.')[0]) if version_date < int(datetime.date.today().strftime('%Y%m%d')): return self._generate_version() else: return pending_version except Exception: return pending_version def version_string_with_vcs(self, always=False): """Return the full version of the package including suffixes indicating VCS status. For instance, if we are working towards the 2012.2 release, canonical_version_string should return 2012.2 if this is a final release, or else something like 2012.2~f1~20120705.20 if it's not. :param always: if true, skip all version caching """ if always: self.version = self._generate_version() if self.version is None: requirement = pkg_resources.Requirement.parse(self.python_package) versioninfo = "%s/versioninfo" % self.package try: raw_version = pkg_resources.resource_string(requirement, versioninfo) self.version = self._newer_version(raw_version.strip()) except (IOError, pkg_resources.DistributionNotFound): self.version = self._generate_version() return self.version def canonical_version_string(self, always=False): """Return the simple version of the package excluding any suffixes. For instance, if we are working towards the 2012.2 release, canonical_version_string should return 2012.2 in all cases. :param always: if true, skip all version caching """ return self.version_string_with_vcs(always).split('~')[0] def version_string(self, always=False): """Return the base version of the package. For instance, if we are working towards the 2012.2 release, version_string should return 2012.2 if this is a final release, or 2012.2-dev if it is not. :param always: if true, skip all version caching """ version_parts = self.version_string_with_vcs(always).split('~') if len(version_parts) == 1: return version_parts[0] else: return '%s-dev' % (version_parts[0],) def deferred_version_string(self, prefix=""): """Generate an object which will expand in a string context to the results of version_string(). We do this so that don't call into pkg_resources every time we start up a program when passing version information into the CONF constructor, but rather only do the calculation when and if a version is requested """ return _deferred_version_string(self, prefix)