Issue #19143: platform module now reads Windows version from kernel32.dll to avoid compatibility shims.
This commit is contained in:
commit
a0c3c19665
286
Lib/platform.py
286
Lib/platform.py
|
@ -26,12 +26,14 @@
|
||||||
# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
|
# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
|
||||||
# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
|
# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
|
||||||
# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
|
# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
|
||||||
# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter
|
# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve
|
||||||
|
# Dower
|
||||||
#
|
#
|
||||||
# History:
|
# History:
|
||||||
#
|
#
|
||||||
# <see CVS and SVN checkin messages for history>
|
# <see CVS and SVN checkin messages for history>
|
||||||
#
|
#
|
||||||
|
# 1.0.8 - changed Windows support to read version from kernel32.dll
|
||||||
# 1.0.7 - added DEV_NULL
|
# 1.0.7 - added DEV_NULL
|
||||||
# 1.0.6 - added linux_distribution()
|
# 1.0.6 - added linux_distribution()
|
||||||
# 1.0.5 - fixed Java support to allow running the module on Jython
|
# 1.0.5 - fixed Java support to allow running the module on Jython
|
||||||
|
@ -469,188 +471,138 @@ def _syscmd_ver(system='', release='', version='',
|
||||||
version = _norm_version(version)
|
version = _norm_version(version)
|
||||||
return system, release, version
|
return system, release, version
|
||||||
|
|
||||||
def _win32_getvalue(key, name, default=''):
|
_WIN32_CLIENT_RELEASES = {
|
||||||
|
(5, 0): "2000",
|
||||||
|
(5, 1): "XP",
|
||||||
|
# Strictly, 5.2 client is XP 64-bit, but platform.py historically
|
||||||
|
# has always called it 2003 Server
|
||||||
|
(5, 2): "2003Server",
|
||||||
|
(5, None): "post2003",
|
||||||
|
|
||||||
""" Read a value for name from the registry key.
|
(6, 0): "Vista",
|
||||||
|
(6, 1): "7",
|
||||||
|
(6, 2): "8",
|
||||||
|
(6, 3): "8.1",
|
||||||
|
(6, None): "post8.1",
|
||||||
|
|
||||||
In case this fails, default is returned.
|
(10, 0): "10",
|
||||||
|
(10, None): "post10",
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
# Server release name lookup will default to client names if necessary
|
||||||
try:
|
_WIN32_SERVER_RELEASES = {
|
||||||
# Use win32api if available
|
(5, 2): "2003Server",
|
||||||
from win32api import RegQueryValueEx
|
|
||||||
except ImportError:
|
(6, 0): "2008Server",
|
||||||
# On Python 2.0 and later, emulate using winreg
|
(6, 1): "2008ServerR2",
|
||||||
import winreg
|
(6, 2): "2012Server",
|
||||||
RegQueryValueEx = winreg.QueryValueEx
|
(6, 3): "2012ServerR2",
|
||||||
try:
|
(6, None): "post2012ServerR2",
|
||||||
return RegQueryValueEx(key, name)
|
}
|
||||||
except:
|
|
||||||
return default
|
def _get_real_winver(maj, min, build):
|
||||||
|
if maj < 6 or (maj == 6 and min < 2):
|
||||||
|
return maj, min, build
|
||||||
|
|
||||||
|
from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer,
|
||||||
|
Structure, WinDLL)
|
||||||
|
from ctypes.wintypes import DWORD, HANDLE
|
||||||
|
|
||||||
|
class VS_FIXEDFILEINFO(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
("dwSignature", DWORD),
|
||||||
|
("dwStrucVersion", DWORD),
|
||||||
|
("dwFileVersionMS", DWORD),
|
||||||
|
("dwFileVersionLS", DWORD),
|
||||||
|
("dwProductVersionMS", DWORD),
|
||||||
|
("dwProductVersionLS", DWORD),
|
||||||
|
("dwFileFlagsMask", DWORD),
|
||||||
|
("dwFileFlags", DWORD),
|
||||||
|
("dwFileOS", DWORD),
|
||||||
|
("dwFileType", DWORD),
|
||||||
|
("dwFileSubtype", DWORD),
|
||||||
|
("dwFileDateMS", DWORD),
|
||||||
|
("dwFileDateLS", DWORD),
|
||||||
|
]
|
||||||
|
|
||||||
|
kernel32 = WinDLL('kernel32')
|
||||||
|
version = WinDLL('version')
|
||||||
|
|
||||||
|
# We will immediately double the length up to MAX_PATH, but the
|
||||||
|
# path may be longer, so we retry until the returned string is
|
||||||
|
# shorter than our buffer.
|
||||||
|
name_len = actual_len = 130
|
||||||
|
while actual_len == name_len:
|
||||||
|
name_len *= 2
|
||||||
|
name = create_unicode_buffer(name_len)
|
||||||
|
actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle),
|
||||||
|
name, len(name))
|
||||||
|
if not actual_len:
|
||||||
|
return maj, min, build
|
||||||
|
|
||||||
|
size = version.GetFileVersionInfoSizeW(name, None)
|
||||||
|
if not size:
|
||||||
|
return maj, min, build
|
||||||
|
|
||||||
|
ver_block = c_buffer(size)
|
||||||
|
if (not version.GetFileVersionInfoW(name, None, size, ver_block) or
|
||||||
|
not ver_block):
|
||||||
|
return maj, min, build
|
||||||
|
|
||||||
|
pvi = POINTER(VS_FIXEDFILEINFO)()
|
||||||
|
if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())):
|
||||||
|
return maj, min, build
|
||||||
|
|
||||||
|
maj = pvi.contents.dwProductVersionMS >> 16
|
||||||
|
min = pvi.contents.dwProductVersionMS & 0xFFFF
|
||||||
|
build = pvi.contents.dwProductVersionLS >> 16
|
||||||
|
|
||||||
|
return maj, min, build
|
||||||
|
|
||||||
def win32_ver(release='', version='', csd='', ptype=''):
|
def win32_ver(release='', version='', csd='', ptype=''):
|
||||||
|
from sys import getwindowsversion
|
||||||
""" Get additional version information from the Windows Registry
|
|
||||||
and return a tuple (version, csd, ptype) referring to version
|
|
||||||
number, CSD level (service pack), and OS type (multi/single
|
|
||||||
processor).
|
|
||||||
|
|
||||||
As a hint: ptype returns 'Uniprocessor Free' on single
|
|
||||||
processor NT machines and 'Multiprocessor Free' on multi
|
|
||||||
processor machines. The 'Free' refers to the OS version being
|
|
||||||
free of debugging code. It could also state 'Checked' which
|
|
||||||
means the OS version uses debugging code, i.e. code that
|
|
||||||
checks arguments, ranges, etc. (Thomas Heller).
|
|
||||||
|
|
||||||
Note: this function works best with Mark Hammond's win32
|
|
||||||
package installed, but also on Python 2.3 and later. It
|
|
||||||
obviously only runs on Win32 compatible platforms.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# XXX Is there any way to find out the processor type on WinXX ?
|
|
||||||
# XXX Is win32 available on Windows CE ?
|
|
||||||
#
|
|
||||||
# Adapted from code posted by Karl Putland to comp.lang.python.
|
|
||||||
#
|
|
||||||
# The mappings between reg. values and release names can be found
|
|
||||||
# here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp
|
|
||||||
|
|
||||||
# Import the needed APIs
|
|
||||||
try:
|
try:
|
||||||
from win32api import RegQueryValueEx, RegOpenKeyEx, \
|
from winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
|
||||||
RegCloseKey, GetVersionEx
|
|
||||||
from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
|
|
||||||
VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Emulate the win32api module using Python APIs
|
from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
|
||||||
try:
|
|
||||||
sys.getwindowsversion
|
|
||||||
except AttributeError:
|
|
||||||
# No emulation possible, so return the defaults...
|
|
||||||
return release, version, csd, ptype
|
|
||||||
else:
|
|
||||||
# Emulation using winreg (added in Python 2.0) and
|
|
||||||
# sys.getwindowsversion() (added in Python 2.3)
|
|
||||||
import winreg
|
|
||||||
GetVersionEx = sys.getwindowsversion
|
|
||||||
RegQueryValueEx = winreg.QueryValueEx
|
|
||||||
RegOpenKeyEx = winreg.OpenKeyEx
|
|
||||||
RegCloseKey = winreg.CloseKey
|
|
||||||
HKEY_LOCAL_MACHINE = winreg.HKEY_LOCAL_MACHINE
|
|
||||||
VER_PLATFORM_WIN32_WINDOWS = 1
|
|
||||||
VER_PLATFORM_WIN32_NT = 2
|
|
||||||
VER_NT_WORKSTATION = 1
|
|
||||||
VER_NT_SERVER = 3
|
|
||||||
REG_SZ = 1
|
|
||||||
|
|
||||||
# Find out the registry key and some general version infos
|
winver = getwindowsversion()
|
||||||
winver = GetVersionEx()
|
maj, min, build = _get_real_winver(*winver[:3])
|
||||||
maj, min, buildno, plat, csd = winver
|
version = '{0}.{1}.{2}'.format(maj, min, build)
|
||||||
version = '%i.%i.%i' % (maj, min, buildno & 0xFFFF)
|
|
||||||
if hasattr(winver, "service_pack"):
|
release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
|
||||||
if winver.service_pack != "":
|
_WIN32_CLIENT_RELEASES.get((maj, None)) or
|
||||||
csd = 'SP%s' % winver.service_pack_major
|
release)
|
||||||
else:
|
|
||||||
|
# getwindowsversion() reflect the compatibility mode Python is
|
||||||
|
# running under, and so the service pack value is only going to be
|
||||||
|
# valid if the versions match.
|
||||||
|
if winver[:2] == (maj, min):
|
||||||
|
try:
|
||||||
|
csd = 'SP{}'.format(winver.service_pack_major)
|
||||||
|
except AttributeError:
|
||||||
if csd[:13] == 'Service Pack ':
|
if csd[:13] == 'Service Pack ':
|
||||||
csd = 'SP' + csd[13:]
|
csd = 'SP' + csd[13:]
|
||||||
|
|
||||||
if plat == VER_PLATFORM_WIN32_WINDOWS:
|
# VER_NT_SERVER = 3
|
||||||
regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
|
if getattr(winver, 'product_type', None) == 3:
|
||||||
# Try to guess the release name
|
release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
|
||||||
if maj == 4:
|
_WIN32_SERVER_RELEASES.get((maj, None)) or
|
||||||
if min == 0:
|
release)
|
||||||
release = '95'
|
|
||||||
elif min == 10:
|
|
||||||
release = '98'
|
|
||||||
elif min == 90:
|
|
||||||
release = 'Me'
|
|
||||||
else:
|
|
||||||
release = 'postMe'
|
|
||||||
elif maj == 5:
|
|
||||||
release = '2000'
|
|
||||||
|
|
||||||
elif plat == VER_PLATFORM_WIN32_NT:
|
key = None
|
||||||
regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
|
|
||||||
if maj <= 4:
|
|
||||||
release = 'NT'
|
|
||||||
elif maj == 5:
|
|
||||||
if min == 0:
|
|
||||||
release = '2000'
|
|
||||||
elif min == 1:
|
|
||||||
release = 'XP'
|
|
||||||
elif min == 2:
|
|
||||||
release = '2003Server'
|
|
||||||
else:
|
|
||||||
release = 'post2003'
|
|
||||||
elif maj == 6:
|
|
||||||
if hasattr(winver, "product_type"):
|
|
||||||
product_type = winver.product_type
|
|
||||||
else:
|
|
||||||
product_type = VER_NT_WORKSTATION
|
|
||||||
# Without an OSVERSIONINFOEX capable sys.getwindowsversion(),
|
|
||||||
# or help from the registry, we cannot properly identify
|
|
||||||
# non-workstation versions.
|
|
||||||
try:
|
try:
|
||||||
key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
|
key = OpenKeyEx(HKEY_LOCAL_MACHINE,
|
||||||
name, type = RegQueryValueEx(key, "ProductName")
|
r'SOFTWARE\Microsoft\Windows NT\CurrentVersion')
|
||||||
# Discard any type that isn't REG_SZ
|
ptype = QueryValueEx(key, 'CurrentType')[0]
|
||||||
if type == REG_SZ and name.find("Server") != -1:
|
|
||||||
product_type = VER_NT_SERVER
|
|
||||||
except OSError:
|
|
||||||
# Use default of VER_NT_WORKSTATION
|
|
||||||
pass
|
|
||||||
|
|
||||||
if min == 0:
|
|
||||||
if product_type == VER_NT_WORKSTATION:
|
|
||||||
release = 'Vista'
|
|
||||||
else:
|
|
||||||
release = '2008Server'
|
|
||||||
elif min == 1:
|
|
||||||
if product_type == VER_NT_WORKSTATION:
|
|
||||||
release = '7'
|
|
||||||
else:
|
|
||||||
release = '2008ServerR2'
|
|
||||||
elif min == 2:
|
|
||||||
if product_type == VER_NT_WORKSTATION:
|
|
||||||
release = '8'
|
|
||||||
else:
|
|
||||||
release = '2012Server'
|
|
||||||
else:
|
|
||||||
release = 'post2012Server'
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not release:
|
|
||||||
# E.g. Win3.1 with win32s
|
|
||||||
release = '%i.%i' % (maj, min)
|
|
||||||
return release, version, csd, ptype
|
|
||||||
|
|
||||||
# Open the registry key
|
|
||||||
try:
|
|
||||||
keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
|
|
||||||
# Get a value to make sure the key exists...
|
|
||||||
RegQueryValueEx(keyCurVer, 'SystemRoot')
|
|
||||||
except:
|
except:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
if key:
|
||||||
|
CloseKey(key)
|
||||||
|
|
||||||
return release, version, csd, ptype
|
return release, version, csd, ptype
|
||||||
|
|
||||||
# Parse values
|
|
||||||
#subversion = _win32_getvalue(keyCurVer,
|
|
||||||
# 'SubVersionNumber',
|
|
||||||
# ('',1))[0]
|
|
||||||
#if subversion:
|
|
||||||
# release = release + subversion # 95a, 95b, etc.
|
|
||||||
build = _win32_getvalue(keyCurVer,
|
|
||||||
'CurrentBuildNumber',
|
|
||||||
('', 1))[0]
|
|
||||||
ptype = _win32_getvalue(keyCurVer,
|
|
||||||
'CurrentType',
|
|
||||||
(ptype, 1))[0]
|
|
||||||
|
|
||||||
# Normalize version
|
|
||||||
version = _norm_version(version, build)
|
|
||||||
|
|
||||||
# Close key
|
|
||||||
RegCloseKey(keyCurVer)
|
|
||||||
return release, version, csd, ptype
|
|
||||||
|
|
||||||
def _mac_ver_xml():
|
def _mac_ver_xml():
|
||||||
fn = '/System/Library/CoreServices/SystemVersion.plist'
|
fn = '/System/Library/CoreServices/SystemVersion.plist'
|
||||||
|
|
|
@ -138,6 +138,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #19143: platform module now reads Windows version from kernel32.dll to
|
||||||
|
avoid compatibility shims.
|
||||||
|
|
||||||
- Issue #25092: Fix datetime.strftime() failure when errno was already set to
|
- Issue #25092: Fix datetime.strftime() failure when errno was already set to
|
||||||
EINVAL.
|
EINVAL.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue