1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
# Copyright (C) 2012 Linaro Limited
#
# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
#
# This file is part of versiontools.
#
# versiontools is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation
#
# versiontools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with versiontools. If not, see <http://www.gnu.org/licenses/>.
"""
versiontools.versiontools_support
=================================
A small standalone module that allows any package to use versiontools.
Typically you should copy this file verbatim into your source distribution.
Historically versiontools was depending on a exotic feature of setuptools to
work. Setuptools has so-called setup-time dependencies, that is modules that
need to be downloaded and imported/interrogated for setup.py to run
successfully. Versiontools supports this by installing a handler for the
'version' keyword of the setup() function.
This approach was always a little annoying as this setuptools feature is rather
odd and very few other packages made any use of it. In the future the standard
tools for python packaging (especially in python3 world) this feature may be
removed or have equivalent thus rendering versiontools completely broken.
Currently the biggest practical issue is the apparent inability to prevent
setuptools from downloading packages designated as setup_requires. This is
discussed in this pip issue: https://github.com/pypa/pip/issues/410
To counter this issue I've redesigned versiontools to be a little smarter. The
old mode stays as-is for compatibility. The new mode works differently, without
the need for using setup_requires in your setup() call. Instead it requires
each package that uses versiontools to ship a verbatim copy of this module and
to import it in their setup.py script. This module helps setuptools find
package version in the standard PKG-INFO file that is created for all source
distributions. Remember that you only need this mode when you don't want to add
a dependency on versiontools. This will still allow you to use versiontools (in
a limited way) in your setup.py file.
Technically this module defines an improved version of one of
distutils.dist.DistributionMetadata class and monkey-patches distutils to use
it. To retain backward compatibility the new feature is only active when a
special version string is passed to the setup() call.
"""
__version__ = (1, 0, 0, "final", 0)
import distutils.dist
import distutils.errors
class VersiontoolsEnchancedDistributionMetadata(distutils.dist.DistributionMetadata):
"""
A subclass of distutils.dist.DistributionMetadata that uses versiontools
Typically you would not instantiate this class directly. It is constructed
by distutils.dist.Distribution.__init__() method. Since there is no other
way to do it, this module monkey-patches distutils to override the original
version of DistributionMetadata
"""
# Reference to the original class. This is only required because distutils
# was created before the introduction of new-style classes to python.
__base = distutils.dist.DistributionMetadata
def get_version(self):
"""
Get distribution version.
This method is enhanced compared to original distutils implementation.
If the version string is set to a special value then instead of using
the actual value the real version is obtained by querying versiontools.
If versiontools package is not installed then the version is obtained
from the standard section of the ``PKG-INFO`` file. This file is
automatically created by any source distribution. This method is less
useful as it cannot take advantage of version control information that
is automatically loaded by versiontools. It has the advantage of not
requiring versiontools installation and that it does not depend on
``setup_requires`` feature of ``setuptools``.
"""
if (self.name is not None and self.version is not None
and self.version.startswith(":versiontools:")):
return (self.__get_live_version() or self.__get_frozen_version()
or self.__fail_to_get_any_version())
else:
return self.__base.get_version(self)
def __get_live_version(self):
"""
Get a live version string using versiontools
"""
try:
import versiontools
except ImportError:
return None
else:
return str(versiontools.Version.from_expression(self.name))
def __get_frozen_version(self):
"""
Get a fixed version string using an existing PKG-INFO file
"""
try:
return self.__base("PKG-INFO").version
except IOError:
return None
def __fail_to_get_any_version(self):
"""
Raise an informative exception
"""
raise SystemExit(
"""This package requires versiontools for development or testing.
See http://versiontools.readthedocs.org/ for more information about
what versiontools is and why it is useful.
To install versiontools now please run:
$ pip install versiontools
Note: versiontools works best when you have additional modules for
integrating with your preferred version control system. Refer to
the documentation for a full list of required modules.""")
# If DistributionMetadata is not a subclass of
# VersiontoolsEnhancedDistributionMetadata then monkey patch it. This should
# prevent a (odd) case of multiple imports of this module.
if not issubclass(
distutils.dist.DistributionMetadata,
VersiontoolsEnchancedDistributionMetadata):
distutils.dist.DistributionMetadata = VersiontoolsEnchancedDistributionMetadata
|