1
2
3
4 """
5 Overview
6 ========
7
8 The ``grizzled.system`` module contains some functions and classes that
9 provide information about the Python system (the Python runtime, the language,
10 etc.). It is a conceptual extension of the standard Python ``sys`` module.
11 """
12
13 from __future__ import absolute_import
14
15 __docformat__ = "restructuredtext en"
16
17
18
19
20
21 import sys as _sys
22 import logging
23 import re
24
25
26
27
28
29 __all__ = ['python_version', 'python_version_string', 'ensure_version',
30 'split_python_version', 'class_for_name']
31
32
33
34
35
36 RELEASE_LEVEL_RE = re.compile(r'([0-9]+)(.[0-9]+)?')
37 RELEASE_LEVELS = {'a' : 0xa, 'b' : 0xb, 'c' : 0xc, 'f' : 0xf}
38 RELEASE_LEVEL_NAMES = {0xa : 'alpha',
39 0xb : 'beta',
40 0xc : 'candidate',
41 0xf : 'final'}
42
43
44
45
46
47 log = logging.getLogger('grizzled.system')
48
49
50
51
52
53
54
55
56
58 """
59 Convert a Python string version (e.g., "2.5.1", "1.3", "2.6a3") to a
60 numeric version that can meaningfully be compared with the standard
61 ``sys`` module's ``sys.hexversion`` value.
62
63 For example, here's the usual way to ensure that your program is running
64 under Python 2.5.1 or better:
65
66 .. python::
67
68 import sys
69
70 if sys.hexversion < 0x020501f0:
71 raise RuntimeError, 'This program requires Python 2.5.1 or better'
72
73 Here's how you'd use ``python_version()`` to do the same thing (in an
74 arguably more readable way):
75
76 .. python::
77
78 import sys
79 from grizzled.sys import python_version
80
81 if sys.hexversion < python_version("2.5.1"):
82 raise RuntimeError, 'This program requires Python 2.5.1 or better'
83
84 :Parameters:
85 version : str
86 string Python version to convert to binary
87
88 :rtype: int
89 :return: corresponding integer Python version
90
91 :raise ValueError: ``version`` isn't of the form "x", or "x.y" or
92 "x.y.z"
93 """
94 err = 'Malformed Python version "%s"' % version
95
96 tokens = version.split('.')
97 if len(tokens) > 3:
98 raise ValueError, err
99
100 major = int(tokens[0])
101 minor = micro = serial = 0
102 release_level = 'f'
103
104 if len(tokens) > 1:
105 match = RELEASE_LEVEL_RE.match(tokens[1])
106 if not match:
107 raise ValueError, err
108
109 minor = int(match.group(1))
110 rl = match.group(2)
111 if rl:
112 release_level = rl[0]
113 serial = int(rl[1:])
114
115 if len(tokens) > 2:
116 match = RELEASE_LEVEL_RE.match(tokens[2])
117 if not match:
118 raise ValueError, err
119
120 micro = int(match.group(1))
121 rl2 = match.group(2)
122 if rl and rl2:
123 raise ValueError, err
124 if rl2:
125 release_level = rl2[0]
126 serial = int(rl2[1:])
127
128 try:
129 release_level = RELEASE_LEVELS[release_level]
130 except KeyError:
131 raise ValueError, err
132
133 return (major << 24) |\
134 (minor << 16) |\
135 (micro << 8) |\
136 (release_level << 4) |\
137 serial
138
140 """
141 Convert a binary Python version string (e.g., ``0x020501f0``) into the
142 same (*major*, *minor*, *micro*, *releaselevel*, *serial*) tuple that is
143 found in ``sys.version_info``. Thus, for an input value of ``0x020501f0``,
144 this function returns the tuple ``(2, 5, 1, 'final', 0)``.
145
146 :Parameters:
147 version : int
148 Python integer version
149
150 :rtype: tuple
151 :return: The (*major*, *minor*, *micro*, *releaselevel*, *serial*) tuple
152
153 :raise ValueError: Bad version number
154 """
155 major = (version >> 24) & 0x000000ff
156 minor = (version >> 16) & 0x000000ff
157 micro = (version >> 8) & 0x000000ff
158 release_level = (version >> 4) & 0x0000000f
159 serial = version & 0x0000000f
160
161 release_level_string = RELEASE_LEVEL_NAMES.get(release_level, None)
162 if not release_level_string:
163 raise ValueError, \
164 'Bad release level 0x%x in version 0x%08x' %\
165 (release_level, version)
166
167 return (major, minor, micro, release_level_string, serial)
168
170 """
171 Convert a numeric Python version (such as ``sys.hexversion``) to a
172 printable string.
173
174 :Parameters:
175 version : int
176 Python integer version
177
178 :rtype: str
179 :return: The stringified version
180 """
181 major, minor, micro, release_level, serial = split_python_version(version)
182 s = '%d.%d' % (major, minor)
183 if micro > 0:
184 s += '.%d' % micro
185
186 if release_level != 'final':
187 s += release_level[0]
188 s += '%s' % serial
189
190 return s
191
193 """
194 Raise a ``RuntimeError`` if the current Python version isn't at least
195 ``min_version``. ``min_version`` may be an ``int`` (e.g., ``0x020500f0``)
196 or a string (e.g., "2.5.0").
197
198 :Parameters:
199 min_version : str or int
200 minimum version, as a number or string
201
202 :raise TypeError: ``min_version`` isn't a string or an integer
203 :raise ValueError: ``min_version`` is a bad Python string version
204 :raise RuntimeError: Python version is too old
205 """
206 if type(min_version) == str:
207 min_version = python_version(min_version)
208 elif type(min_version) == int:
209 pass
210 else:
211 raise TypeError, \
212 'version %s is not a string or an integer' % min_version
213
214 if _sys.hexversion < min_version:
215 raise RuntimeError, \
216 'This program requires Python version "%s" or better, but ' \
217 'the current Python version is "%s".' %\
218 (python_version_string(min_version),
219 python_version_string(sys.hexversion))
220
221
223 """
224 Given fully-qualified class name, load and return the class object. A
225 fully-qualified class name contains the module and package, in addition to
226 the simple class name (e.g., ``grizzled.config.Configuration``).
227
228 :Parameters:
229 class_name : str
230 fully-qualified class name
231
232 :rtype: class
233 :return: the class object
234
235 :raise NameError: Class not found
236 """
237 tokens = class_name.split('.')
238 if len(tokens) > 1:
239 package = '.'.join(tokens[:-1])
240 class_name = tokens[-1]
241 exec 'from %s import %s' % (package, class_name)
242
243 return eval(class_name)
244