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