]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | #!/usr/bin/env python\r |
2 | \r | |
3 | """ This module tries to retrieve as much platform-identifying data as\r | |
4 | possible. It makes this information available via function APIs.\r | |
5 | \r | |
6 | If called from the command line, it prints the platform\r | |
7 | information concatenated as single string to stdout. The output\r | |
8 | format is useable as part of a filename.\r | |
9 | \r | |
10 | """\r | |
11 | # This module is maintained by Marc-Andre Lemburg <mal@egenix.com>.\r | |
12 | # If you find problems, please submit bug reports/patches via the\r | |
13 | # Python bug tracker (http://bugs.python.org) and assign them to "lemburg".\r | |
14 | #\r | |
15 | # Note: Please keep this module compatible to Python 1.5.2.\r | |
16 | #\r | |
17 | # Still needed:\r | |
18 | # * more support for WinCE\r | |
19 | # * support for MS-DOS (PythonDX ?)\r | |
20 | # * support for Amiga and other still unsupported platforms running Python\r | |
21 | # * support for additional Linux distributions\r | |
22 | #\r | |
23 | # Many thanks to all those who helped adding platform-specific\r | |
24 | # checks (in no particular order):\r | |
25 | #\r | |
26 | # Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell,\r | |
27 | # Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef\r | |
28 | # Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg\r | |
29 | # Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark\r | |
30 | # Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),\r | |
31 | # Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter\r | |
32 | #\r | |
33 | # History:\r | |
34 | #\r | |
35 | # <see CVS and SVN checkin messages for history>\r | |
36 | #\r | |
37 | # 1.0.7 - added DEV_NULL\r | |
38 | # 1.0.6 - added linux_distribution()\r | |
39 | # 1.0.5 - fixed Java support to allow running the module on Jython\r | |
40 | # 1.0.4 - added IronPython support\r | |
41 | # 1.0.3 - added normalization of Windows system name\r | |
42 | # 1.0.2 - added more Windows support\r | |
43 | # 1.0.1 - reformatted to make doc.py happy\r | |
44 | # 1.0.0 - reformatted a bit and checked into Python CVS\r | |
45 | # 0.8.0 - added sys.version parser and various new access\r | |
46 | # APIs (python_version(), python_compiler(), etc.)\r | |
47 | # 0.7.2 - fixed architecture() to use sizeof(pointer) where available\r | |
48 | # 0.7.1 - added support for Caldera OpenLinux\r | |
49 | # 0.7.0 - some fixes for WinCE; untabified the source file\r | |
50 | # 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and\r | |
51 | # vms_lib.getsyi() configured\r | |
52 | # 0.6.1 - added code to prevent 'uname -p' on platforms which are\r | |
53 | # known not to support it\r | |
54 | # 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k;\r | |
55 | # did some cleanup of the interfaces - some APIs have changed\r | |
56 | # 0.5.5 - fixed another type in the MacOS code... should have\r | |
57 | # used more coffee today ;-)\r | |
58 | # 0.5.4 - fixed a few typos in the MacOS code\r | |
59 | # 0.5.3 - added experimental MacOS support; added better popen()\r | |
60 | # workarounds in _syscmd_ver() -- still not 100% elegant\r | |
61 | # though\r | |
62 | # 0.5.2 - fixed uname() to return '' instead of 'unknown' in all\r | |
63 | # return values (the system uname command tends to return\r | |
64 | # 'unknown' instead of just leaving the field emtpy)\r | |
65 | # 0.5.1 - included code for slackware dist; added exception handlers\r | |
66 | # to cover up situations where platforms don't have os.popen\r | |
67 | # (e.g. Mac) or fail on socket.gethostname(); fixed libc\r | |
68 | # detection RE\r | |
69 | # 0.5.0 - changed the API names referring to system commands to *syscmd*;\r | |
70 | # added java_ver(); made syscmd_ver() a private\r | |
71 | # API (was system_ver() in previous versions) -- use uname()\r | |
72 | # instead; extended the win32_ver() to also return processor\r | |
73 | # type information\r | |
74 | # 0.4.0 - added win32_ver() and modified the platform() output for WinXX\r | |
75 | # 0.3.4 - fixed a bug in _follow_symlinks()\r | |
76 | # 0.3.3 - fixed popen() and "file" command invokation bugs\r | |
77 | # 0.3.2 - added architecture() API and support for it in platform()\r | |
78 | # 0.3.1 - fixed syscmd_ver() RE to support Windows NT\r | |
79 | # 0.3.0 - added system alias support\r | |
80 | # 0.2.3 - removed 'wince' again... oh well.\r | |
81 | # 0.2.2 - added 'wince' to syscmd_ver() supported platforms\r | |
82 | # 0.2.1 - added cache logic and changed the platform string format\r | |
83 | # 0.2.0 - changed the API to use functions instead of module globals\r | |
84 | # since some action take too long to be run on module import\r | |
85 | # 0.1.0 - first release\r | |
86 | #\r | |
87 | # You can always get the latest version of this module at:\r | |
88 | #\r | |
89 | # http://www.egenix.com/files/python/platform.py\r | |
90 | #\r | |
91 | # If that URL should fail, try contacting the author.\r | |
92 | \r | |
93 | __copyright__ = """\r | |
94 | Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com\r | |
95 | Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com\r | |
96 | \r | |
97 | Permission to use, copy, modify, and distribute this software and its\r | |
98 | documentation for any purpose and without fee or royalty is hereby granted,\r | |
99 | provided that the above copyright notice appear in all copies and that\r | |
100 | both that copyright notice and this permission notice appear in\r | |
101 | supporting documentation or portions thereof, including modifications,\r | |
102 | that you make.\r | |
103 | \r | |
104 | EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO\r | |
105 | THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\r | |
106 | FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL,\r | |
107 | INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING\r | |
108 | FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,\r | |
109 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION\r | |
110 | WITH THE USE OR PERFORMANCE OF THIS SOFTWARE !\r | |
111 | \r | |
112 | """\r | |
113 | \r | |
114 | __version__ = '1.0.7'\r | |
115 | \r | |
116 | import sys,string,os,re\r | |
117 | \r | |
118 | ### Globals & Constants\r | |
119 | \r | |
120 | # Determine the platform's /dev/null device\r | |
121 | try:\r | |
122 | DEV_NULL = os.devnull\r | |
123 | except AttributeError:\r | |
124 | # os.devnull was added in Python 2.4, so emulate it for earlier\r | |
125 | # Python versions\r | |
126 | if sys.platform in ('dos','win32','win16','os2'):\r | |
127 | # Use the old CP/M NUL as device name\r | |
128 | DEV_NULL = 'NUL'\r | |
129 | else:\r | |
130 | # Standard Unix uses /dev/null\r | |
131 | DEV_NULL = '/dev/null'\r | |
132 | \r | |
133 | ### Platform specific APIs\r | |
134 | \r | |
135 | _libc_search = re.compile(r'(__libc_init)'\r | |
136 | '|'\r | |
137 | '(GLIBC_([0-9.]+))'\r | |
138 | '|'\r | |
139 | '(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)')\r | |
140 | \r | |
141 | def libc_ver(executable=sys.executable,lib='',version='',\r | |
142 | \r | |
143 | chunksize=2048):\r | |
144 | \r | |
145 | """ Tries to determine the libc version that the file executable\r | |
146 | (which defaults to the Python interpreter) is linked against.\r | |
147 | \r | |
148 | Returns a tuple of strings (lib,version) which default to the\r | |
149 | given parameters in case the lookup fails.\r | |
150 | \r | |
151 | Note that the function has intimate knowledge of how different\r | |
152 | libc versions add symbols to the executable and thus is probably\r | |
153 | only useable for executables compiled using gcc.\r | |
154 | \r | |
155 | The file is read and scanned in chunks of chunksize bytes.\r | |
156 | \r | |
157 | """\r | |
158 | if hasattr(os.path, 'realpath'):\r | |
159 | # Python 2.2 introduced os.path.realpath(); it is used\r | |
160 | # here to work around problems with Cygwin not being\r | |
161 | # able to open symlinks for reading\r | |
162 | executable = os.path.realpath(executable)\r | |
163 | f = open(executable,'rb')\r | |
164 | binary = f.read(chunksize)\r | |
165 | pos = 0\r | |
166 | while 1:\r | |
167 | m = _libc_search.search(binary,pos)\r | |
168 | if not m:\r | |
169 | binary = f.read(chunksize)\r | |
170 | if not binary:\r | |
171 | break\r | |
172 | pos = 0\r | |
173 | continue\r | |
174 | libcinit,glibc,glibcversion,so,threads,soversion = m.groups()\r | |
175 | if libcinit and not lib:\r | |
176 | lib = 'libc'\r | |
177 | elif glibc:\r | |
178 | if lib != 'glibc':\r | |
179 | lib = 'glibc'\r | |
180 | version = glibcversion\r | |
181 | elif glibcversion > version:\r | |
182 | version = glibcversion\r | |
183 | elif so:\r | |
184 | if lib != 'glibc':\r | |
185 | lib = 'libc'\r | |
186 | if soversion > version:\r | |
187 | version = soversion\r | |
188 | if threads and version[-len(threads):] != threads:\r | |
189 | version = version + threads\r | |
190 | pos = m.end()\r | |
191 | f.close()\r | |
192 | return lib,version\r | |
193 | \r | |
194 | def _dist_try_harder(distname,version,id):\r | |
195 | \r | |
196 | """ Tries some special tricks to get the distribution\r | |
197 | information in case the default method fails.\r | |
198 | \r | |
199 | Currently supports older SuSE Linux, Caldera OpenLinux and\r | |
200 | Slackware Linux distributions.\r | |
201 | \r | |
202 | """\r | |
203 | if os.path.exists('/var/adm/inst-log/info'):\r | |
204 | # SuSE Linux stores distribution information in that file\r | |
205 | info = open('/var/adm/inst-log/info').readlines()\r | |
206 | distname = 'SuSE'\r | |
207 | for line in info:\r | |
208 | tv = string.split(line)\r | |
209 | if len(tv) == 2:\r | |
210 | tag,value = tv\r | |
211 | else:\r | |
212 | continue\r | |
213 | if tag == 'MIN_DIST_VERSION':\r | |
214 | version = string.strip(value)\r | |
215 | elif tag == 'DIST_IDENT':\r | |
216 | values = string.split(value,'-')\r | |
217 | id = values[2]\r | |
218 | return distname,version,id\r | |
219 | \r | |
220 | if os.path.exists('/etc/.installed'):\r | |
221 | # Caldera OpenLinux has some infos in that file (thanks to Colin Kong)\r | |
222 | info = open('/etc/.installed').readlines()\r | |
223 | for line in info:\r | |
224 | pkg = string.split(line,'-')\r | |
225 | if len(pkg) >= 2 and pkg[0] == 'OpenLinux':\r | |
226 | # XXX does Caldera support non Intel platforms ? If yes,\r | |
227 | # where can we find the needed id ?\r | |
228 | return 'OpenLinux',pkg[1],id\r | |
229 | \r | |
230 | if os.path.isdir('/usr/lib/setup'):\r | |
231 | # Check for slackware verson tag file (thanks to Greg Andruk)\r | |
232 | verfiles = os.listdir('/usr/lib/setup')\r | |
233 | for n in range(len(verfiles)-1, -1, -1):\r | |
234 | if verfiles[n][:14] != 'slack-version-':\r | |
235 | del verfiles[n]\r | |
236 | if verfiles:\r | |
237 | verfiles.sort()\r | |
238 | distname = 'slackware'\r | |
239 | version = verfiles[-1][14:]\r | |
240 | return distname,version,id\r | |
241 | \r | |
242 | return distname,version,id\r | |
243 | \r | |
244 | _release_filename = re.compile(r'(\w+)[-_](release|version)')\r | |
245 | _lsb_release_version = re.compile(r'(.+)'\r | |
246 | ' release '\r | |
247 | '([\d.]+)'\r | |
248 | '[^(]*(?:\((.+)\))?')\r | |
249 | _release_version = re.compile(r'([^0-9]+)'\r | |
250 | '(?: release )?'\r | |
251 | '([\d.]+)'\r | |
252 | '[^(]*(?:\((.+)\))?')\r | |
253 | \r | |
254 | # See also http://www.novell.com/coolsolutions/feature/11251.html\r | |
255 | # and http://linuxmafia.com/faq/Admin/release-files.html\r | |
256 | # and http://data.linux-ntfs.org/rpm/whichrpm\r | |
257 | # and http://www.die.net/doc/linux/man/man1/lsb_release.1.html\r | |
258 | \r | |
259 | _supported_dists = (\r | |
260 | 'SuSE', 'debian', 'fedora', 'redhat', 'centos',\r | |
261 | 'mandrake', 'mandriva', 'rocks', 'slackware', 'yellowdog', 'gentoo',\r | |
262 | 'UnitedLinux', 'turbolinux')\r | |
263 | \r | |
264 | def _parse_release_file(firstline):\r | |
265 | \r | |
266 | # Default to empty 'version' and 'id' strings. Both defaults are used\r | |
267 | # when 'firstline' is empty. 'id' defaults to empty when an id can not\r | |
268 | # be deduced.\r | |
269 | version = ''\r | |
270 | id = ''\r | |
271 | \r | |
272 | # Parse the first line\r | |
273 | m = _lsb_release_version.match(firstline)\r | |
274 | if m is not None:\r | |
275 | # LSB format: "distro release x.x (codename)"\r | |
276 | return tuple(m.groups())\r | |
277 | \r | |
278 | # Pre-LSB format: "distro x.x (codename)"\r | |
279 | m = _release_version.match(firstline)\r | |
280 | if m is not None:\r | |
281 | return tuple(m.groups())\r | |
282 | \r | |
283 | # Unkown format... take the first two words\r | |
284 | l = string.split(string.strip(firstline))\r | |
285 | if l:\r | |
286 | version = l[0]\r | |
287 | if len(l) > 1:\r | |
288 | id = l[1]\r | |
289 | return '', version, id\r | |
290 | \r | |
291 | def linux_distribution(distname='', version='', id='',\r | |
292 | \r | |
293 | supported_dists=_supported_dists,\r | |
294 | full_distribution_name=1):\r | |
295 | \r | |
296 | """ Tries to determine the name of the Linux OS distribution name.\r | |
297 | \r | |
298 | The function first looks for a distribution release file in\r | |
299 | /etc and then reverts to _dist_try_harder() in case no\r | |
300 | suitable files are found.\r | |
301 | \r | |
302 | supported_dists may be given to define the set of Linux\r | |
303 | distributions to look for. It defaults to a list of currently\r | |
304 | supported Linux distributions identified by their release file\r | |
305 | name.\r | |
306 | \r | |
307 | If full_distribution_name is true (default), the full\r | |
308 | distribution read from the OS is returned. Otherwise the short\r | |
309 | name taken from supported_dists is used.\r | |
310 | \r | |
311 | Returns a tuple (distname,version,id) which default to the\r | |
312 | args given as parameters.\r | |
313 | \r | |
314 | """\r | |
315 | try:\r | |
316 | etc = os.listdir('/etc')\r | |
317 | except os.error:\r | |
318 | # Probably not a Unix system\r | |
319 | return distname,version,id\r | |
320 | etc.sort()\r | |
321 | for file in etc:\r | |
322 | m = _release_filename.match(file)\r | |
323 | if m is not None:\r | |
324 | _distname,dummy = m.groups()\r | |
325 | if _distname in supported_dists:\r | |
326 | distname = _distname\r | |
327 | break\r | |
328 | else:\r | |
329 | return _dist_try_harder(distname,version,id)\r | |
330 | \r | |
331 | # Read the first line\r | |
332 | f = open('/etc/'+file, 'r')\r | |
333 | firstline = f.readline()\r | |
334 | f.close()\r | |
335 | _distname, _version, _id = _parse_release_file(firstline)\r | |
336 | \r | |
337 | if _distname and full_distribution_name:\r | |
338 | distname = _distname\r | |
339 | if _version:\r | |
340 | version = _version\r | |
341 | if _id:\r | |
342 | id = _id\r | |
343 | return distname, version, id\r | |
344 | \r | |
345 | # To maintain backwards compatibility:\r | |
346 | \r | |
347 | def dist(distname='',version='',id='',\r | |
348 | \r | |
349 | supported_dists=_supported_dists):\r | |
350 | \r | |
351 | """ Tries to determine the name of the Linux OS distribution name.\r | |
352 | \r | |
353 | The function first looks for a distribution release file in\r | |
354 | /etc and then reverts to _dist_try_harder() in case no\r | |
355 | suitable files are found.\r | |
356 | \r | |
357 | Returns a tuple (distname,version,id) which default to the\r | |
358 | args given as parameters.\r | |
359 | \r | |
360 | """\r | |
361 | return linux_distribution(distname, version, id,\r | |
362 | supported_dists=supported_dists,\r | |
363 | full_distribution_name=0)\r | |
364 | \r | |
365 | class _popen:\r | |
366 | \r | |
367 | """ Fairly portable (alternative) popen implementation.\r | |
368 | \r | |
369 | This is mostly needed in case os.popen() is not available, or\r | |
370 | doesn't work as advertised, e.g. in Win9X GUI programs like\r | |
371 | PythonWin or IDLE.\r | |
372 | \r | |
373 | Writing to the pipe is currently not supported.\r | |
374 | \r | |
375 | """\r | |
376 | tmpfile = ''\r | |
377 | pipe = None\r | |
378 | bufsize = None\r | |
379 | mode = 'r'\r | |
380 | \r | |
381 | def __init__(self,cmd,mode='r',bufsize=None):\r | |
382 | \r | |
383 | if mode != 'r':\r | |
384 | raise ValueError,'popen()-emulation only supports read mode'\r | |
385 | import tempfile\r | |
386 | self.tmpfile = tmpfile = tempfile.mktemp()\r | |
387 | os.system(cmd + ' > %s' % tmpfile)\r | |
388 | self.pipe = open(tmpfile,'rb')\r | |
389 | self.bufsize = bufsize\r | |
390 | self.mode = mode\r | |
391 | \r | |
392 | def read(self):\r | |
393 | \r | |
394 | return self.pipe.read()\r | |
395 | \r | |
396 | def readlines(self):\r | |
397 | \r | |
398 | if self.bufsize is not None:\r | |
399 | return self.pipe.readlines()\r | |
400 | \r | |
401 | def close(self,\r | |
402 | \r | |
403 | remove=os.unlink,error=os.error):\r | |
404 | \r | |
405 | if self.pipe:\r | |
406 | rc = self.pipe.close()\r | |
407 | else:\r | |
408 | rc = 255\r | |
409 | if self.tmpfile:\r | |
410 | try:\r | |
411 | remove(self.tmpfile)\r | |
412 | except error:\r | |
413 | pass\r | |
414 | return rc\r | |
415 | \r | |
416 | # Alias\r | |
417 | __del__ = close\r | |
418 | \r | |
419 | def popen(cmd, mode='r', bufsize=None):\r | |
420 | \r | |
421 | """ Portable popen() interface.\r | |
422 | """\r | |
423 | # Find a working popen implementation preferring win32pipe.popen\r | |
424 | # over os.popen over _popen\r | |
425 | popen = None\r | |
426 | if os.environ.get('OS','') == 'Windows_NT':\r | |
427 | # On NT win32pipe should work; on Win9x it hangs due to bugs\r | |
428 | # in the MS C lib (see MS KnowledgeBase article Q150956)\r | |
429 | try:\r | |
430 | import win32pipe\r | |
431 | except ImportError:\r | |
432 | pass\r | |
433 | else:\r | |
434 | popen = win32pipe.popen\r | |
435 | if popen is None:\r | |
436 | if hasattr(os,'popen'):\r | |
437 | popen = os.popen\r | |
438 | # Check whether it works... it doesn't in GUI programs\r | |
439 | # on Windows platforms\r | |
440 | if sys.platform == 'win32': # XXX Others too ?\r | |
441 | try:\r | |
442 | popen('')\r | |
443 | except os.error:\r | |
444 | popen = _popen\r | |
445 | else:\r | |
446 | popen = _popen\r | |
447 | if bufsize is None:\r | |
448 | return popen(cmd,mode)\r | |
449 | else:\r | |
450 | return popen(cmd,mode,bufsize)\r | |
451 | \r | |
452 | def _norm_version(version, build=''):\r | |
453 | \r | |
454 | """ Normalize the version and build strings and return a single\r | |
455 | version string using the format major.minor.build (or patchlevel).\r | |
456 | """\r | |
457 | l = string.split(version,'.')\r | |
458 | if build:\r | |
459 | l.append(build)\r | |
460 | try:\r | |
461 | ints = map(int,l)\r | |
462 | except ValueError:\r | |
463 | strings = l\r | |
464 | else:\r | |
465 | strings = map(str,ints)\r | |
466 | version = string.join(strings[:3],'.')\r | |
467 | return version\r | |
468 | \r | |
469 | _ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) '\r | |
470 | '.*'\r | |
471 | '\[.* ([\d.]+)\])')\r | |
472 | \r | |
473 | # Examples of VER command output:\r | |
474 | #\r | |
475 | # Windows 2000: Microsoft Windows 2000 [Version 5.00.2195]\r | |
476 | # Windows XP: Microsoft Windows XP [Version 5.1.2600]\r | |
477 | # Windows Vista: Microsoft Windows [Version 6.0.6002]\r | |
478 | #\r | |
479 | # Note that the "Version" string gets localized on different\r | |
480 | # Windows versions.\r | |
481 | \r | |
482 | def _syscmd_ver(system='', release='', version='',\r | |
483 | \r | |
484 | supported_platforms=('win32','win16','dos','os2')):\r | |
485 | \r | |
486 | """ Tries to figure out the OS version used and returns\r | |
487 | a tuple (system,release,version).\r | |
488 | \r | |
489 | It uses the "ver" shell command for this which is known\r | |
490 | to exists on Windows, DOS and OS/2. XXX Others too ?\r | |
491 | \r | |
492 | In case this fails, the given parameters are used as\r | |
493 | defaults.\r | |
494 | \r | |
495 | """\r | |
496 | if sys.platform not in supported_platforms:\r | |
497 | return system,release,version\r | |
498 | \r | |
499 | # Try some common cmd strings\r | |
500 | for cmd in ('ver','command /c ver','cmd /c ver'):\r | |
501 | try:\r | |
502 | pipe = popen(cmd)\r | |
503 | info = pipe.read()\r | |
504 | if pipe.close():\r | |
505 | raise os.error,'command failed'\r | |
506 | # XXX How can I suppress shell errors from being written\r | |
507 | # to stderr ?\r | |
508 | except os.error,why:\r | |
509 | #print 'Command %s failed: %s' % (cmd,why)\r | |
510 | continue\r | |
511 | except IOError,why:\r | |
512 | #print 'Command %s failed: %s' % (cmd,why)\r | |
513 | continue\r | |
514 | else:\r | |
515 | break\r | |
516 | else:\r | |
517 | return system,release,version\r | |
518 | \r | |
519 | # Parse the output\r | |
520 | info = string.strip(info)\r | |
521 | m = _ver_output.match(info)\r | |
522 | if m is not None:\r | |
523 | system,release,version = m.groups()\r | |
524 | # Strip trailing dots from version and release\r | |
525 | if release[-1] == '.':\r | |
526 | release = release[:-1]\r | |
527 | if version[-1] == '.':\r | |
528 | version = version[:-1]\r | |
529 | # Normalize the version and build strings (eliminating additional\r | |
530 | # zeros)\r | |
531 | version = _norm_version(version)\r | |
532 | return system,release,version\r | |
533 | \r | |
534 | def _win32_getvalue(key,name,default=''):\r | |
535 | \r | |
536 | """ Read a value for name from the registry key.\r | |
537 | \r | |
538 | In case this fails, default is returned.\r | |
539 | \r | |
540 | """\r | |
541 | try:\r | |
542 | # Use win32api if available\r | |
543 | from win32api import RegQueryValueEx\r | |
544 | except ImportError:\r | |
545 | # On Python 2.0 and later, emulate using _winreg\r | |
546 | import _winreg\r | |
547 | RegQueryValueEx = _winreg.QueryValueEx\r | |
548 | try:\r | |
549 | return RegQueryValueEx(key,name)\r | |
550 | except:\r | |
551 | return default\r | |
552 | \r | |
553 | def win32_ver(release='',version='',csd='',ptype=''):\r | |
554 | \r | |
555 | """ Get additional version information from the Windows Registry\r | |
556 | and return a tuple (version,csd,ptype) referring to version\r | |
557 | number, CSD level and OS type (multi/single\r | |
558 | processor).\r | |
559 | \r | |
560 | As a hint: ptype returns 'Uniprocessor Free' on single\r | |
561 | processor NT machines and 'Multiprocessor Free' on multi\r | |
562 | processor machines. The 'Free' refers to the OS version being\r | |
563 | free of debugging code. It could also state 'Checked' which\r | |
564 | means the OS version uses debugging code, i.e. code that\r | |
565 | checks arguments, ranges, etc. (Thomas Heller).\r | |
566 | \r | |
567 | Note: this function works best with Mark Hammond's win32\r | |
568 | package installed, but also on Python 2.3 and later. It\r | |
569 | obviously only runs on Win32 compatible platforms.\r | |
570 | \r | |
571 | """\r | |
572 | # XXX Is there any way to find out the processor type on WinXX ?\r | |
573 | # XXX Is win32 available on Windows CE ?\r | |
574 | #\r | |
575 | # Adapted from code posted by Karl Putland to comp.lang.python.\r | |
576 | #\r | |
577 | # The mappings between reg. values and release names can be found\r | |
578 | # here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp\r | |
579 | \r | |
580 | # Import the needed APIs\r | |
581 | try:\r | |
582 | import win32api\r | |
583 | from win32api import RegQueryValueEx, RegOpenKeyEx, \\r | |
584 | RegCloseKey, GetVersionEx\r | |
585 | from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \\r | |
586 | VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION\r | |
587 | except ImportError:\r | |
588 | # Emulate the win32api module using Python APIs\r | |
589 | try:\r | |
590 | sys.getwindowsversion\r | |
591 | except AttributeError:\r | |
592 | # No emulation possible, so return the defaults...\r | |
593 | return release,version,csd,ptype\r | |
594 | else:\r | |
595 | # Emulation using _winreg (added in Python 2.0) and\r | |
596 | # sys.getwindowsversion() (added in Python 2.3)\r | |
597 | import _winreg\r | |
598 | GetVersionEx = sys.getwindowsversion\r | |
599 | RegQueryValueEx = _winreg.QueryValueEx\r | |
600 | RegOpenKeyEx = _winreg.OpenKeyEx\r | |
601 | RegCloseKey = _winreg.CloseKey\r | |
602 | HKEY_LOCAL_MACHINE = _winreg.HKEY_LOCAL_MACHINE\r | |
603 | VER_PLATFORM_WIN32_WINDOWS = 1\r | |
604 | VER_PLATFORM_WIN32_NT = 2\r | |
605 | VER_NT_WORKSTATION = 1\r | |
606 | VER_NT_SERVER = 3\r | |
607 | REG_SZ = 1\r | |
608 | \r | |
609 | # Find out the registry key and some general version infos\r | |
610 | winver = GetVersionEx()\r | |
611 | maj,min,buildno,plat,csd = winver\r | |
612 | version = '%i.%i.%i' % (maj,min,buildno & 0xFFFF)\r | |
613 | if hasattr(winver, "service_pack"):\r | |
614 | if winver.service_pack != "":\r | |
615 | csd = 'SP%s' % winver.service_pack_major\r | |
616 | else:\r | |
617 | if csd[:13] == 'Service Pack ':\r | |
618 | csd = 'SP' + csd[13:]\r | |
619 | \r | |
620 | if plat == VER_PLATFORM_WIN32_WINDOWS:\r | |
621 | regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'\r | |
622 | # Try to guess the release name\r | |
623 | if maj == 4:\r | |
624 | if min == 0:\r | |
625 | release = '95'\r | |
626 | elif min == 10:\r | |
627 | release = '98'\r | |
628 | elif min == 90:\r | |
629 | release = 'Me'\r | |
630 | else:\r | |
631 | release = 'postMe'\r | |
632 | elif maj == 5:\r | |
633 | release = '2000'\r | |
634 | \r | |
635 | elif plat == VER_PLATFORM_WIN32_NT:\r | |
636 | regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'\r | |
637 | if maj <= 4:\r | |
638 | release = 'NT'\r | |
639 | elif maj == 5:\r | |
640 | if min == 0:\r | |
641 | release = '2000'\r | |
642 | elif min == 1:\r | |
643 | release = 'XP'\r | |
644 | elif min == 2:\r | |
645 | release = '2003Server'\r | |
646 | else:\r | |
647 | release = 'post2003'\r | |
648 | elif maj == 6:\r | |
649 | if hasattr(winver, "product_type"):\r | |
650 | product_type = winver.product_type\r | |
651 | else:\r | |
652 | product_type = VER_NT_WORKSTATION\r | |
653 | # Without an OSVERSIONINFOEX capable sys.getwindowsversion(),\r | |
654 | # or help from the registry, we cannot properly identify\r | |
655 | # non-workstation versions.\r | |
656 | try:\r | |
657 | key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)\r | |
658 | name, type = RegQueryValueEx(key, "ProductName")\r | |
659 | # Discard any type that isn't REG_SZ\r | |
660 | if type == REG_SZ and name.find("Server") != -1:\r | |
661 | product_type = VER_NT_SERVER\r | |
662 | except WindowsError:\r | |
663 | # Use default of VER_NT_WORKSTATION\r | |
664 | pass\r | |
665 | \r | |
666 | if min == 0:\r | |
667 | if product_type == VER_NT_WORKSTATION:\r | |
668 | release = 'Vista'\r | |
669 | else:\r | |
670 | release = '2008Server'\r | |
671 | elif min == 1:\r | |
672 | if product_type == VER_NT_WORKSTATION:\r | |
673 | release = '7'\r | |
674 | else:\r | |
675 | release = '2008ServerR2'\r | |
676 | else:\r | |
677 | release = 'post2008Server'\r | |
678 | \r | |
679 | else:\r | |
680 | if not release:\r | |
681 | # E.g. Win3.1 with win32s\r | |
682 | release = '%i.%i' % (maj,min)\r | |
683 | return release,version,csd,ptype\r | |
684 | \r | |
685 | # Open the registry key\r | |
686 | try:\r | |
687 | keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)\r | |
688 | # Get a value to make sure the key exists...\r | |
689 | RegQueryValueEx(keyCurVer, 'SystemRoot')\r | |
690 | except:\r | |
691 | return release,version,csd,ptype\r | |
692 | \r | |
693 | # Parse values\r | |
694 | #subversion = _win32_getvalue(keyCurVer,\r | |
695 | # 'SubVersionNumber',\r | |
696 | # ('',1))[0]\r | |
697 | #if subversion:\r | |
698 | # release = release + subversion # 95a, 95b, etc.\r | |
699 | build = _win32_getvalue(keyCurVer,\r | |
700 | 'CurrentBuildNumber',\r | |
701 | ('',1))[0]\r | |
702 | ptype = _win32_getvalue(keyCurVer,\r | |
703 | 'CurrentType',\r | |
704 | (ptype,1))[0]\r | |
705 | \r | |
706 | # Normalize version\r | |
707 | version = _norm_version(version,build)\r | |
708 | \r | |
709 | # Close key\r | |
710 | RegCloseKey(keyCurVer)\r | |
711 | return release,version,csd,ptype\r | |
712 | \r | |
713 | def _mac_ver_lookup(selectors,default=None):\r | |
714 | \r | |
715 | from gestalt import gestalt\r | |
716 | import MacOS\r | |
717 | l = []\r | |
718 | append = l.append\r | |
719 | for selector in selectors:\r | |
720 | try:\r | |
721 | append(gestalt(selector))\r | |
722 | except (RuntimeError, MacOS.Error):\r | |
723 | append(default)\r | |
724 | return l\r | |
725 | \r | |
726 | def _bcd2str(bcd):\r | |
727 | \r | |
728 | return hex(bcd)[2:]\r | |
729 | \r | |
730 | def _mac_ver_gestalt():\r | |
731 | """\r | |
732 | Thanks to Mark R. Levinson for mailing documentation links and\r | |
733 | code examples for this function. Documentation for the\r | |
734 | gestalt() API is available online at:\r | |
735 | \r | |
736 | http://www.rgaros.nl/gestalt/\r | |
737 | """\r | |
738 | # Check whether the version info module is available\r | |
739 | try:\r | |
740 | import gestalt\r | |
741 | import MacOS\r | |
742 | except ImportError:\r | |
743 | return None\r | |
744 | # Get the infos\r | |
745 | sysv,sysa = _mac_ver_lookup(('sysv','sysa'))\r | |
746 | # Decode the infos\r | |
747 | if sysv:\r | |
748 | major = (sysv & 0xFF00) >> 8\r | |
749 | minor = (sysv & 0x00F0) >> 4\r | |
750 | patch = (sysv & 0x000F)\r | |
751 | \r | |
752 | if (major, minor) >= (10, 4):\r | |
753 | # the 'sysv' gestald cannot return patchlevels\r | |
754 | # higher than 9. Apple introduced 3 new\r | |
755 | # gestalt codes in 10.4 to deal with this\r | |
756 | # issue (needed because patch levels can\r | |
757 | # run higher than 9, such as 10.4.11)\r | |
758 | major,minor,patch = _mac_ver_lookup(('sys1','sys2','sys3'))\r | |
759 | release = '%i.%i.%i' %(major, minor, patch)\r | |
760 | else:\r | |
761 | release = '%s.%i.%i' % (_bcd2str(major),minor,patch)\r | |
762 | \r | |
763 | if sysa:\r | |
764 | machine = {0x1: '68k',\r | |
765 | 0x2: 'PowerPC',\r | |
766 | 0xa: 'i386'}.get(sysa,'')\r | |
767 | \r | |
768 | return release,versioninfo,machine\r | |
769 | \r | |
770 | def _mac_ver_xml():\r | |
771 | fn = '/System/Library/CoreServices/SystemVersion.plist'\r | |
772 | if not os.path.exists(fn):\r | |
773 | return None\r | |
774 | \r | |
775 | try:\r | |
776 | import plistlib\r | |
777 | except ImportError:\r | |
778 | return None\r | |
779 | \r | |
780 | pl = plistlib.readPlist(fn)\r | |
781 | release = pl['ProductVersion']\r | |
782 | versioninfo=('', '', '')\r | |
783 | machine = os.uname()[4]\r | |
784 | if machine in ('ppc', 'Power Macintosh'):\r | |
785 | # for compatibility with the gestalt based code\r | |
786 | machine = 'PowerPC'\r | |
787 | \r | |
788 | return release,versioninfo,machine\r | |
789 | \r | |
790 | \r | |
791 | def mac_ver(release='',versioninfo=('','',''),machine=''):\r | |
792 | \r | |
793 | """ Get MacOS version information and return it as tuple (release,\r | |
794 | versioninfo, machine) with versioninfo being a tuple (version,\r | |
795 | dev_stage, non_release_version).\r | |
796 | \r | |
797 | Entries which cannot be determined are set to the paramter values\r | |
798 | which default to ''. All tuple entries are strings.\r | |
799 | """\r | |
800 | \r | |
801 | # First try reading the information from an XML file which should\r | |
802 | # always be present\r | |
803 | info = _mac_ver_xml()\r | |
804 | if info is not None:\r | |
805 | return info\r | |
806 | \r | |
807 | # If that doesn't work for some reason fall back to reading the\r | |
808 | # information using gestalt calls.\r | |
809 | info = _mac_ver_gestalt()\r | |
810 | if info is not None:\r | |
811 | return info\r | |
812 | \r | |
813 | # If that also doesn't work return the default values\r | |
814 | return release,versioninfo,machine\r | |
815 | \r | |
816 | def _java_getprop(name,default):\r | |
817 | \r | |
818 | from java.lang import System\r | |
819 | try:\r | |
820 | value = System.getProperty(name)\r | |
821 | if value is None:\r | |
822 | return default\r | |
823 | return value\r | |
824 | except AttributeError:\r | |
825 | return default\r | |
826 | \r | |
827 | def java_ver(release='',vendor='',vminfo=('','',''),osinfo=('','','')):\r | |
828 | \r | |
829 | """ Version interface for Jython.\r | |
830 | \r | |
831 | Returns a tuple (release,vendor,vminfo,osinfo) with vminfo being\r | |
832 | a tuple (vm_name,vm_release,vm_vendor) and osinfo being a\r | |
833 | tuple (os_name,os_version,os_arch).\r | |
834 | \r | |
835 | Values which cannot be determined are set to the defaults\r | |
836 | given as parameters (which all default to '').\r | |
837 | \r | |
838 | """\r | |
839 | # Import the needed APIs\r | |
840 | try:\r | |
841 | import java.lang\r | |
842 | except ImportError:\r | |
843 | return release,vendor,vminfo,osinfo\r | |
844 | \r | |
845 | vendor = _java_getprop('java.vendor', vendor)\r | |
846 | release = _java_getprop('java.version', release)\r | |
847 | vm_name, vm_release, vm_vendor = vminfo\r | |
848 | vm_name = _java_getprop('java.vm.name', vm_name)\r | |
849 | vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)\r | |
850 | vm_release = _java_getprop('java.vm.version', vm_release)\r | |
851 | vminfo = vm_name, vm_release, vm_vendor\r | |
852 | os_name, os_version, os_arch = osinfo\r | |
853 | os_arch = _java_getprop('java.os.arch', os_arch)\r | |
854 | os_name = _java_getprop('java.os.name', os_name)\r | |
855 | os_version = _java_getprop('java.os.version', os_version)\r | |
856 | osinfo = os_name, os_version, os_arch\r | |
857 | \r | |
858 | return release, vendor, vminfo, osinfo\r | |
859 | \r | |
860 | ### System name aliasing\r | |
861 | \r | |
862 | def system_alias(system,release,version):\r | |
863 | \r | |
864 | """ Returns (system,release,version) aliased to common\r | |
865 | marketing names used for some systems.\r | |
866 | \r | |
867 | It also does some reordering of the information in some cases\r | |
868 | where it would otherwise cause confusion.\r | |
869 | \r | |
870 | """\r | |
871 | if system == 'Rhapsody':\r | |
872 | # Apple's BSD derivative\r | |
873 | # XXX How can we determine the marketing release number ?\r | |
874 | return 'MacOS X Server',system+release,version\r | |
875 | \r | |
876 | elif system == 'SunOS':\r | |
877 | # Sun's OS\r | |
878 | if release < '5':\r | |
879 | # These releases use the old name SunOS\r | |
880 | return system,release,version\r | |
881 | # Modify release (marketing release = SunOS release - 3)\r | |
882 | l = string.split(release,'.')\r | |
883 | if l:\r | |
884 | try:\r | |
885 | major = int(l[0])\r | |
886 | except ValueError:\r | |
887 | pass\r | |
888 | else:\r | |
889 | major = major - 3\r | |
890 | l[0] = str(major)\r | |
891 | release = string.join(l,'.')\r | |
892 | if release < '6':\r | |
893 | system = 'Solaris'\r | |
894 | else:\r | |
895 | # XXX Whatever the new SunOS marketing name is...\r | |
896 | system = 'Solaris'\r | |
897 | \r | |
898 | elif system == 'IRIX64':\r | |
899 | # IRIX reports IRIX64 on platforms with 64-bit support; yet it\r | |
900 | # is really a version and not a different platform, since 32-bit\r | |
901 | # apps are also supported..\r | |
902 | system = 'IRIX'\r | |
903 | if version:\r | |
904 | version = version + ' (64bit)'\r | |
905 | else:\r | |
906 | version = '64bit'\r | |
907 | \r | |
908 | elif system in ('win32','win16'):\r | |
909 | # In case one of the other tricks\r | |
910 | system = 'Windows'\r | |
911 | \r | |
912 | return system,release,version\r | |
913 | \r | |
914 | ### Various internal helpers\r | |
915 | \r | |
916 | def _platform(*args):\r | |
917 | \r | |
918 | """ Helper to format the platform string in a filename\r | |
919 | compatible format e.g. "system-version-machine".\r | |
920 | """\r | |
921 | # Format the platform string\r | |
922 | platform = string.join(\r | |
923 | map(string.strip,\r | |
924 | filter(len, args)),\r | |
925 | '-')\r | |
926 | \r | |
927 | # Cleanup some possible filename obstacles...\r | |
928 | replace = string.replace\r | |
929 | platform = replace(platform,' ','_')\r | |
930 | platform = replace(platform,'/','-')\r | |
931 | platform = replace(platform,'\\','-')\r | |
932 | platform = replace(platform,':','-')\r | |
933 | platform = replace(platform,';','-')\r | |
934 | platform = replace(platform,'"','-')\r | |
935 | platform = replace(platform,'(','-')\r | |
936 | platform = replace(platform,')','-')\r | |
937 | \r | |
938 | # No need to report 'unknown' information...\r | |
939 | platform = replace(platform,'unknown','')\r | |
940 | \r | |
941 | # Fold '--'s and remove trailing '-'\r | |
942 | while 1:\r | |
943 | cleaned = replace(platform,'--','-')\r | |
944 | if cleaned == platform:\r | |
945 | break\r | |
946 | platform = cleaned\r | |
947 | while platform[-1] == '-':\r | |
948 | platform = platform[:-1]\r | |
949 | \r | |
950 | return platform\r | |
951 | \r | |
952 | def _node(default=''):\r | |
953 | \r | |
954 | """ Helper to determine the node name of this machine.\r | |
955 | """\r | |
956 | try:\r | |
957 | import socket\r | |
958 | except ImportError:\r | |
959 | # No sockets...\r | |
960 | return default\r | |
961 | try:\r | |
962 | return socket.gethostname()\r | |
963 | except socket.error:\r | |
964 | # Still not working...\r | |
965 | return default\r | |
966 | \r | |
967 | # os.path.abspath is new in Python 1.5.2:\r | |
968 | if not hasattr(os.path,'abspath'):\r | |
969 | \r | |
970 | def _abspath(path,\r | |
971 | \r | |
972 | isabs=os.path.isabs,join=os.path.join,getcwd=os.getcwd,\r | |
973 | normpath=os.path.normpath):\r | |
974 | \r | |
975 | if not isabs(path):\r | |
976 | path = join(getcwd(), path)\r | |
977 | return normpath(path)\r | |
978 | \r | |
979 | else:\r | |
980 | \r | |
981 | _abspath = os.path.abspath\r | |
982 | \r | |
983 | def _follow_symlinks(filepath):\r | |
984 | \r | |
985 | """ In case filepath is a symlink, follow it until a\r | |
986 | real file is reached.\r | |
987 | """\r | |
988 | filepath = _abspath(filepath)\r | |
989 | while os.path.islink(filepath):\r | |
990 | filepath = os.path.normpath(\r | |
991 | os.path.join(os.path.dirname(filepath),os.readlink(filepath)))\r | |
992 | return filepath\r | |
993 | \r | |
994 | def _syscmd_uname(option,default=''):\r | |
995 | \r | |
996 | """ Interface to the system's uname command.\r | |
997 | """\r | |
998 | if sys.platform in ('dos','win32','win16','os2'):\r | |
999 | # XXX Others too ?\r | |
1000 | return default\r | |
1001 | try:\r | |
1002 | f = os.popen('uname %s 2> %s' % (option, DEV_NULL))\r | |
1003 | except (AttributeError,os.error):\r | |
1004 | return default\r | |
1005 | output = string.strip(f.read())\r | |
1006 | rc = f.close()\r | |
1007 | if not output or rc:\r | |
1008 | return default\r | |
1009 | else:\r | |
1010 | return output\r | |
1011 | \r | |
1012 | def _syscmd_file(target,default=''):\r | |
1013 | \r | |
1014 | """ Interface to the system's file command.\r | |
1015 | \r | |
1016 | The function uses the -b option of the file command to have it\r | |
1017 | ommit the filename in its output and if possible the -L option\r | |
1018 | to have the command follow symlinks. It returns default in\r | |
1019 | case the command should fail.\r | |
1020 | \r | |
1021 | """\r | |
1022 | if sys.platform in ('dos','win32','win16','os2'):\r | |
1023 | # XXX Others too ?\r | |
1024 | return default\r | |
1025 | target = _follow_symlinks(target).replace('"', '\\"')\r | |
1026 | try:\r | |
1027 | f = os.popen('file "%s" 2> %s' % (target, DEV_NULL))\r | |
1028 | except (AttributeError,os.error):\r | |
1029 | return default\r | |
1030 | output = string.strip(f.read())\r | |
1031 | rc = f.close()\r | |
1032 | if not output or rc:\r | |
1033 | return default\r | |
1034 | else:\r | |
1035 | return output\r | |
1036 | \r | |
1037 | ### Information about the used architecture\r | |
1038 | \r | |
1039 | # Default values for architecture; non-empty strings override the\r | |
1040 | # defaults given as parameters\r | |
1041 | _default_architecture = {\r | |
1042 | 'win32': ('','WindowsPE'),\r | |
1043 | 'win16': ('','Windows'),\r | |
1044 | 'dos': ('','MSDOS'),\r | |
1045 | }\r | |
1046 | \r | |
1047 | _architecture_split = re.compile(r'[\s,]').split\r | |
1048 | \r | |
1049 | def architecture(executable=sys.executable,bits='',linkage=''):\r | |
1050 | \r | |
1051 | """ Queries the given executable (defaults to the Python interpreter\r | |
1052 | binary) for various architecture information.\r | |
1053 | \r | |
1054 | Returns a tuple (bits,linkage) which contains information about\r | |
1055 | the bit architecture and the linkage format used for the\r | |
1056 | executable. Both values are returned as strings.\r | |
1057 | \r | |
1058 | Values that cannot be determined are returned as given by the\r | |
1059 | parameter presets. If bits is given as '', the sizeof(pointer)\r | |
1060 | (or sizeof(long) on Python version < 1.5.2) is used as\r | |
1061 | indicator for the supported pointer size.\r | |
1062 | \r | |
1063 | The function relies on the system's "file" command to do the\r | |
1064 | actual work. This is available on most if not all Unix\r | |
1065 | platforms. On some non-Unix platforms where the "file" command\r | |
1066 | does not exist and the executable is set to the Python interpreter\r | |
1067 | binary defaults from _default_architecture are used.\r | |
1068 | \r | |
1069 | """\r | |
1070 | # Use the sizeof(pointer) as default number of bits if nothing\r | |
1071 | # else is given as default.\r | |
1072 | if not bits:\r | |
1073 | import struct\r | |
1074 | try:\r | |
1075 | size = struct.calcsize('P')\r | |
1076 | except struct.error:\r | |
1077 | # Older installations can only query longs\r | |
1078 | size = struct.calcsize('l')\r | |
1079 | bits = str(size*8) + 'bit'\r | |
1080 | \r | |
1081 | # Get data from the 'file' system command\r | |
1082 | if executable:\r | |
1083 | output = _syscmd_file(executable, '')\r | |
1084 | else:\r | |
1085 | output = ''\r | |
1086 | \r | |
1087 | if not output and \\r | |
1088 | executable == sys.executable:\r | |
1089 | # "file" command did not return anything; we'll try to provide\r | |
1090 | # some sensible defaults then...\r | |
1091 | if sys.platform in _default_architecture:\r | |
1092 | b, l = _default_architecture[sys.platform]\r | |
1093 | if b:\r | |
1094 | bits = b\r | |
1095 | if l:\r | |
1096 | linkage = l\r | |
1097 | return bits, linkage\r | |
1098 | \r | |
1099 | # Split the output into a list of strings omitting the filename\r | |
1100 | fileout = _architecture_split(output)[1:]\r | |
1101 | \r | |
1102 | if 'executable' not in fileout:\r | |
1103 | # Format not supported\r | |
1104 | return bits,linkage\r | |
1105 | \r | |
1106 | # Bits\r | |
1107 | if '32-bit' in fileout:\r | |
1108 | bits = '32bit'\r | |
1109 | elif 'N32' in fileout:\r | |
1110 | # On Irix only\r | |
1111 | bits = 'n32bit'\r | |
1112 | elif '64-bit' in fileout:\r | |
1113 | bits = '64bit'\r | |
1114 | \r | |
1115 | # Linkage\r | |
1116 | if 'ELF' in fileout:\r | |
1117 | linkage = 'ELF'\r | |
1118 | elif 'PE' in fileout:\r | |
1119 | # E.g. Windows uses this format\r | |
1120 | if 'Windows' in fileout:\r | |
1121 | linkage = 'WindowsPE'\r | |
1122 | else:\r | |
1123 | linkage = 'PE'\r | |
1124 | elif 'COFF' in fileout:\r | |
1125 | linkage = 'COFF'\r | |
1126 | elif 'MS-DOS' in fileout:\r | |
1127 | linkage = 'MSDOS'\r | |
1128 | else:\r | |
1129 | # XXX the A.OUT format also falls under this class...\r | |
1130 | pass\r | |
1131 | \r | |
1132 | return bits,linkage\r | |
1133 | \r | |
1134 | ### Portable uname() interface\r | |
1135 | \r | |
1136 | _uname_cache = None\r | |
1137 | \r | |
1138 | def uname():\r | |
1139 | \r | |
1140 | """ Fairly portable uname interface. Returns a tuple\r | |
1141 | of strings (system,node,release,version,machine,processor)\r | |
1142 | identifying the underlying platform.\r | |
1143 | \r | |
1144 | Note that unlike the os.uname function this also returns\r | |
1145 | possible processor information as an additional tuple entry.\r | |
1146 | \r | |
1147 | Entries which cannot be determined are set to ''.\r | |
1148 | \r | |
1149 | """\r | |
1150 | global _uname_cache\r | |
1151 | no_os_uname = 0\r | |
1152 | \r | |
1153 | if _uname_cache is not None:\r | |
1154 | return _uname_cache\r | |
1155 | \r | |
1156 | processor = ''\r | |
1157 | \r | |
1158 | # Get some infos from the builtin os.uname API...\r | |
1159 | try:\r | |
1160 | system,node,release,version,machine = os.uname()\r | |
1161 | except AttributeError:\r | |
1162 | no_os_uname = 1\r | |
1163 | \r | |
1164 | if no_os_uname or not filter(None, (system, node, release, version, machine)):\r | |
1165 | # Hmm, no there is either no uname or uname has returned\r | |
1166 | #'unknowns'... we'll have to poke around the system then.\r | |
1167 | if no_os_uname:\r | |
1168 | system = sys.platform\r | |
1169 | release = ''\r | |
1170 | version = ''\r | |
1171 | node = _node()\r | |
1172 | machine = ''\r | |
1173 | \r | |
1174 | use_syscmd_ver = 1\r | |
1175 | \r | |
1176 | # Try win32_ver() on win32 platforms\r | |
1177 | if system == 'win32':\r | |
1178 | release,version,csd,ptype = win32_ver()\r | |
1179 | if release and version:\r | |
1180 | use_syscmd_ver = 0\r | |
1181 | # Try to use the PROCESSOR_* environment variables\r | |
1182 | # available on Win XP and later; see\r | |
1183 | # http://support.microsoft.com/kb/888731 and\r | |
1184 | # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM\r | |
1185 | if not machine:\r | |
1186 | # WOW64 processes mask the native architecture\r | |
1187 | if "PROCESSOR_ARCHITEW6432" in os.environ:\r | |
1188 | machine = os.environ.get("PROCESSOR_ARCHITEW6432", '')\r | |
1189 | else:\r | |
1190 | machine = os.environ.get('PROCESSOR_ARCHITECTURE', '')\r | |
1191 | if not processor:\r | |
1192 | processor = os.environ.get('PROCESSOR_IDENTIFIER', machine)\r | |
1193 | \r | |
1194 | # Try the 'ver' system command available on some\r | |
1195 | # platforms\r | |
1196 | if use_syscmd_ver:\r | |
1197 | system,release,version = _syscmd_ver(system)\r | |
1198 | # Normalize system to what win32_ver() normally returns\r | |
1199 | # (_syscmd_ver() tends to return the vendor name as well)\r | |
1200 | if system == 'Microsoft Windows':\r | |
1201 | system = 'Windows'\r | |
1202 | elif system == 'Microsoft' and release == 'Windows':\r | |
1203 | # Under Windows Vista and Windows Server 2008,\r | |
1204 | # Microsoft changed the output of the ver command. The\r | |
1205 | # release is no longer printed. This causes the\r | |
1206 | # system and release to be misidentified.\r | |
1207 | system = 'Windows'\r | |
1208 | if '6.0' == version[:3]:\r | |
1209 | release = 'Vista'\r | |
1210 | else:\r | |
1211 | release = ''\r | |
1212 | \r | |
1213 | # In case we still don't know anything useful, we'll try to\r | |
1214 | # help ourselves\r | |
1215 | if system in ('win32','win16'):\r | |
1216 | if not version:\r | |
1217 | if system == 'win32':\r | |
1218 | version = '32bit'\r | |
1219 | else:\r | |
1220 | version = '16bit'\r | |
1221 | system = 'Windows'\r | |
1222 | \r | |
1223 | elif system[:4] == 'java':\r | |
1224 | release,vendor,vminfo,osinfo = java_ver()\r | |
1225 | system = 'Java'\r | |
1226 | version = string.join(vminfo,', ')\r | |
1227 | if not version:\r | |
1228 | version = vendor\r | |
1229 | \r | |
1230 | # System specific extensions\r | |
1231 | if system == 'OpenVMS':\r | |
1232 | # OpenVMS seems to have release and version mixed up\r | |
1233 | if not release or release == '0':\r | |
1234 | release = version\r | |
1235 | version = ''\r | |
1236 | # Get processor information\r | |
1237 | try:\r | |
1238 | import vms_lib\r | |
1239 | except ImportError:\r | |
1240 | pass\r | |
1241 | else:\r | |
1242 | csid, cpu_number = vms_lib.getsyi('SYI$_CPU',0)\r | |
1243 | if (cpu_number >= 128):\r | |
1244 | processor = 'Alpha'\r | |
1245 | else:\r | |
1246 | processor = 'VAX'\r | |
1247 | if not processor:\r | |
1248 | # Get processor information from the uname system command\r | |
1249 | processor = _syscmd_uname('-p','')\r | |
1250 | \r | |
1251 | #If any unknowns still exist, replace them with ''s, which are more portable\r | |
1252 | if system == 'unknown':\r | |
1253 | system = ''\r | |
1254 | if node == 'unknown':\r | |
1255 | node = ''\r | |
1256 | if release == 'unknown':\r | |
1257 | release = ''\r | |
1258 | if version == 'unknown':\r | |
1259 | version = ''\r | |
1260 | if machine == 'unknown':\r | |
1261 | machine = ''\r | |
1262 | if processor == 'unknown':\r | |
1263 | processor = ''\r | |
1264 | \r | |
1265 | # normalize name\r | |
1266 | if system == 'Microsoft' and release == 'Windows':\r | |
1267 | system = 'Windows'\r | |
1268 | release = 'Vista'\r | |
1269 | \r | |
1270 | _uname_cache = system,node,release,version,machine,processor\r | |
1271 | return _uname_cache\r | |
1272 | \r | |
1273 | ### Direct interfaces to some of the uname() return values\r | |
1274 | \r | |
1275 | def system():\r | |
1276 | \r | |
1277 | """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'.\r | |
1278 | \r | |
1279 | An empty string is returned if the value cannot be determined.\r | |
1280 | \r | |
1281 | """\r | |
1282 | return uname()[0]\r | |
1283 | \r | |
1284 | def node():\r | |
1285 | \r | |
1286 | """ Returns the computer's network name (which may not be fully\r | |
1287 | qualified)\r | |
1288 | \r | |
1289 | An empty string is returned if the value cannot be determined.\r | |
1290 | \r | |
1291 | """\r | |
1292 | return uname()[1]\r | |
1293 | \r | |
1294 | def release():\r | |
1295 | \r | |
1296 | """ Returns the system's release, e.g. '2.2.0' or 'NT'\r | |
1297 | \r | |
1298 | An empty string is returned if the value cannot be determined.\r | |
1299 | \r | |
1300 | """\r | |
1301 | return uname()[2]\r | |
1302 | \r | |
1303 | def version():\r | |
1304 | \r | |
1305 | """ Returns the system's release version, e.g. '#3 on degas'\r | |
1306 | \r | |
1307 | An empty string is returned if the value cannot be determined.\r | |
1308 | \r | |
1309 | """\r | |
1310 | return uname()[3]\r | |
1311 | \r | |
1312 | def machine():\r | |
1313 | \r | |
1314 | """ Returns the machine type, e.g. 'i386'\r | |
1315 | \r | |
1316 | An empty string is returned if the value cannot be determined.\r | |
1317 | \r | |
1318 | """\r | |
1319 | return uname()[4]\r | |
1320 | \r | |
1321 | def processor():\r | |
1322 | \r | |
1323 | """ Returns the (true) processor name, e.g. 'amdk6'\r | |
1324 | \r | |
1325 | An empty string is returned if the value cannot be\r | |
1326 | determined. Note that many platforms do not provide this\r | |
1327 | information or simply return the same value as for machine(),\r | |
1328 | e.g. NetBSD does this.\r | |
1329 | \r | |
1330 | """\r | |
1331 | return uname()[5]\r | |
1332 | \r | |
1333 | ### Various APIs for extracting information from sys.version\r | |
1334 | \r | |
1335 | _sys_version_parser = re.compile(\r | |
1336 | r'([\w.+]+)\s*'\r | |
1337 | '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'\r | |
1338 | '\[([^\]]+)\]?')\r | |
1339 | \r | |
1340 | _ironpython_sys_version_parser = re.compile(\r | |
1341 | r'IronPython\s*'\r | |
1342 | '([\d\.]+)'\r | |
1343 | '(?: \(([\d\.]+)\))?'\r | |
1344 | ' on (.NET [\d\.]+)')\r | |
1345 | \r | |
1346 | _pypy_sys_version_parser = re.compile(\r | |
1347 | r'([\w.+]+)\s*'\r | |
1348 | '\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*'\r | |
1349 | '\[PyPy [^\]]+\]?')\r | |
1350 | \r | |
1351 | _sys_version_cache = {}\r | |
1352 | \r | |
1353 | def _sys_version(sys_version=None):\r | |
1354 | \r | |
1355 | """ Returns a parsed version of Python's sys.version as tuple\r | |
1356 | (name, version, branch, revision, buildno, builddate, compiler)\r | |
1357 | referring to the Python implementation name, version, branch,\r | |
1358 | revision, build number, build date/time as string and the compiler\r | |
1359 | identification string.\r | |
1360 | \r | |
1361 | Note that unlike the Python sys.version, the returned value\r | |
1362 | for the Python version will always include the patchlevel (it\r | |
1363 | defaults to '.0').\r | |
1364 | \r | |
1365 | The function returns empty strings for tuple entries that\r | |
1366 | cannot be determined.\r | |
1367 | \r | |
1368 | sys_version may be given to parse an alternative version\r | |
1369 | string, e.g. if the version was read from a different Python\r | |
1370 | interpreter.\r | |
1371 | \r | |
1372 | """\r | |
1373 | # Get the Python version\r | |
1374 | if sys_version is None:\r | |
1375 | sys_version = sys.version\r | |
1376 | \r | |
1377 | # Try the cache first\r | |
1378 | result = _sys_version_cache.get(sys_version, None)\r | |
1379 | if result is not None:\r | |
1380 | return result\r | |
1381 | \r | |
1382 | # Parse it\r | |
1383 | if sys_version[:10] == 'IronPython':\r | |
1384 | # IronPython\r | |
1385 | name = 'IronPython'\r | |
1386 | match = _ironpython_sys_version_parser.match(sys_version)\r | |
1387 | if match is None:\r | |
1388 | raise ValueError(\r | |
1389 | 'failed to parse IronPython sys.version: %s' %\r | |
1390 | repr(sys_version))\r | |
1391 | version, alt_version, compiler = match.groups()\r | |
1392 | buildno = ''\r | |
1393 | builddate = ''\r | |
1394 | \r | |
1395 | elif sys.platform[:4] == 'java':\r | |
1396 | # Jython\r | |
1397 | name = 'Jython'\r | |
1398 | match = _sys_version_parser.match(sys_version)\r | |
1399 | if match is None:\r | |
1400 | raise ValueError(\r | |
1401 | 'failed to parse Jython sys.version: %s' %\r | |
1402 | repr(sys_version))\r | |
1403 | version, buildno, builddate, buildtime, _ = match.groups()\r | |
1404 | compiler = sys.platform\r | |
1405 | \r | |
1406 | elif "PyPy" in sys_version:\r | |
1407 | # PyPy\r | |
1408 | name = "PyPy"\r | |
1409 | match = _pypy_sys_version_parser.match(sys_version)\r | |
1410 | if match is None:\r | |
1411 | raise ValueError("failed to parse PyPy sys.version: %s" %\r | |
1412 | repr(sys_version))\r | |
1413 | version, buildno, builddate, buildtime = match.groups()\r | |
1414 | compiler = ""\r | |
1415 | \r | |
1416 | else:\r | |
1417 | # CPython\r | |
1418 | match = _sys_version_parser.match(sys_version)\r | |
1419 | if match is None:\r | |
1420 | raise ValueError(\r | |
1421 | 'failed to parse CPython sys.version: %s' %\r | |
1422 | repr(sys_version))\r | |
1423 | version, buildno, builddate, buildtime, compiler = \\r | |
1424 | match.groups()\r | |
1425 | name = 'CPython'\r | |
1426 | builddate = builddate + ' ' + buildtime\r | |
1427 | \r | |
1428 | if hasattr(sys, 'subversion'):\r | |
1429 | # sys.subversion was added in Python 2.5\r | |
1430 | _, branch, revision = sys.subversion\r | |
1431 | else:\r | |
1432 | branch = ''\r | |
1433 | revision = ''\r | |
1434 | \r | |
1435 | # Add the patchlevel version if missing\r | |
1436 | l = string.split(version, '.')\r | |
1437 | if len(l) == 2:\r | |
1438 | l.append('0')\r | |
1439 | version = string.join(l, '.')\r | |
1440 | \r | |
1441 | # Build and cache the result\r | |
1442 | result = (name, version, branch, revision, buildno, builddate, compiler)\r | |
1443 | _sys_version_cache[sys_version] = result\r | |
1444 | return result\r | |
1445 | \r | |
1446 | def python_implementation():\r | |
1447 | \r | |
1448 | """ Returns a string identifying the Python implementation.\r | |
1449 | \r | |
1450 | Currently, the following implementations are identified:\r | |
1451 | 'CPython' (C implementation of Python),\r | |
1452 | 'IronPython' (.NET implementation of Python),\r | |
1453 | 'Jython' (Java implementation of Python),\r | |
1454 | 'PyPy' (Python implementation of Python).\r | |
1455 | \r | |
1456 | """\r | |
1457 | return _sys_version()[0]\r | |
1458 | \r | |
1459 | def python_version():\r | |
1460 | \r | |
1461 | """ Returns the Python version as string 'major.minor.patchlevel'\r | |
1462 | \r | |
1463 | Note that unlike the Python sys.version, the returned value\r | |
1464 | will always include the patchlevel (it defaults to 0).\r | |
1465 | \r | |
1466 | """\r | |
1467 | return _sys_version()[1]\r | |
1468 | \r | |
1469 | def python_version_tuple():\r | |
1470 | \r | |
1471 | """ Returns the Python version as tuple (major, minor, patchlevel)\r | |
1472 | of strings.\r | |
1473 | \r | |
1474 | Note that unlike the Python sys.version, the returned value\r | |
1475 | will always include the patchlevel (it defaults to 0).\r | |
1476 | \r | |
1477 | """\r | |
1478 | return tuple(string.split(_sys_version()[1], '.'))\r | |
1479 | \r | |
1480 | def python_branch():\r | |
1481 | \r | |
1482 | """ Returns a string identifying the Python implementation\r | |
1483 | branch.\r | |
1484 | \r | |
1485 | For CPython this is the Subversion branch from which the\r | |
1486 | Python binary was built.\r | |
1487 | \r | |
1488 | If not available, an empty string is returned.\r | |
1489 | \r | |
1490 | """\r | |
1491 | \r | |
1492 | return _sys_version()[2]\r | |
1493 | \r | |
1494 | def python_revision():\r | |
1495 | \r | |
1496 | """ Returns a string identifying the Python implementation\r | |
1497 | revision.\r | |
1498 | \r | |
1499 | For CPython this is the Subversion revision from which the\r | |
1500 | Python binary was built.\r | |
1501 | \r | |
1502 | If not available, an empty string is returned.\r | |
1503 | \r | |
1504 | """\r | |
1505 | return _sys_version()[3]\r | |
1506 | \r | |
1507 | def python_build():\r | |
1508 | \r | |
1509 | """ Returns a tuple (buildno, builddate) stating the Python\r | |
1510 | build number and date as strings.\r | |
1511 | \r | |
1512 | """\r | |
1513 | return _sys_version()[4:6]\r | |
1514 | \r | |
1515 | def python_compiler():\r | |
1516 | \r | |
1517 | """ Returns a string identifying the compiler used for compiling\r | |
1518 | Python.\r | |
1519 | \r | |
1520 | """\r | |
1521 | return _sys_version()[6]\r | |
1522 | \r | |
1523 | ### The Opus Magnum of platform strings :-)\r | |
1524 | \r | |
1525 | _platform_cache = {}\r | |
1526 | \r | |
1527 | def platform(aliased=0, terse=0):\r | |
1528 | \r | |
1529 | """ Returns a single string identifying the underlying platform\r | |
1530 | with as much useful information as possible (but no more :).\r | |
1531 | \r | |
1532 | The output is intended to be human readable rather than\r | |
1533 | machine parseable. It may look different on different\r | |
1534 | platforms and this is intended.\r | |
1535 | \r | |
1536 | If "aliased" is true, the function will use aliases for\r | |
1537 | various platforms that report system names which differ from\r | |
1538 | their common names, e.g. SunOS will be reported as\r | |
1539 | Solaris. The system_alias() function is used to implement\r | |
1540 | this.\r | |
1541 | \r | |
1542 | Setting terse to true causes the function to return only the\r | |
1543 | absolute minimum information needed to identify the platform.\r | |
1544 | \r | |
1545 | """\r | |
1546 | result = _platform_cache.get((aliased, terse), None)\r | |
1547 | if result is not None:\r | |
1548 | return result\r | |
1549 | \r | |
1550 | # Get uname information and then apply platform specific cosmetics\r | |
1551 | # to it...\r | |
1552 | system,node,release,version,machine,processor = uname()\r | |
1553 | if machine == processor:\r | |
1554 | processor = ''\r | |
1555 | if aliased:\r | |
1556 | system,release,version = system_alias(system,release,version)\r | |
1557 | \r | |
1558 | if system == 'Windows':\r | |
1559 | # MS platforms\r | |
1560 | rel,vers,csd,ptype = win32_ver(version)\r | |
1561 | if terse:\r | |
1562 | platform = _platform(system,release)\r | |
1563 | else:\r | |
1564 | platform = _platform(system,release,version,csd)\r | |
1565 | \r | |
1566 | elif system in ('Linux',):\r | |
1567 | # Linux based systems\r | |
1568 | distname,distversion,distid = dist('')\r | |
1569 | if distname and not terse:\r | |
1570 | platform = _platform(system,release,machine,processor,\r | |
1571 | 'with',\r | |
1572 | distname,distversion,distid)\r | |
1573 | else:\r | |
1574 | # If the distribution name is unknown check for libc vs. glibc\r | |
1575 | libcname,libcversion = libc_ver(sys.executable)\r | |
1576 | platform = _platform(system,release,machine,processor,\r | |
1577 | 'with',\r | |
1578 | libcname+libcversion)\r | |
1579 | elif system == 'Java':\r | |
1580 | # Java platforms\r | |
1581 | r,v,vminfo,(os_name,os_version,os_arch) = java_ver()\r | |
1582 | if terse or not os_name:\r | |
1583 | platform = _platform(system,release,version)\r | |
1584 | else:\r | |
1585 | platform = _platform(system,release,version,\r | |
1586 | 'on',\r | |
1587 | os_name,os_version,os_arch)\r | |
1588 | \r | |
1589 | elif system == 'MacOS':\r | |
1590 | # MacOS platforms\r | |
1591 | if terse:\r | |
1592 | platform = _platform(system,release)\r | |
1593 | else:\r | |
1594 | platform = _platform(system,release,machine)\r | |
1595 | \r | |
1596 | else:\r | |
1597 | # Generic handler\r | |
1598 | if terse:\r | |
1599 | platform = _platform(system,release)\r | |
1600 | else:\r | |
1601 | bits,linkage = architecture(sys.executable)\r | |
1602 | platform = _platform(system,release,machine,processor,bits,linkage)\r | |
1603 | \r | |
1604 | _platform_cache[(aliased, terse)] = platform\r | |
1605 | return platform\r | |
1606 | \r | |
1607 | ### Command line interface\r | |
1608 | \r | |
1609 | if __name__ == '__main__':\r | |
1610 | # Default is to print the aliased verbose platform string\r | |
1611 | terse = ('terse' in sys.argv or '--terse' in sys.argv)\r | |
1612 | aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv)\r | |
1613 | print platform(aliased,terse)\r | |
1614 | sys.exit(0)\r |