Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b9f4fea

Browse filesBrowse files
committed
Issue #19143: platform module now reads Windows version from kernel32.dll to avoid compatibility shims.
1 parent a2ea0e4 commit b9f4fea
Copy full SHA for b9f4fea

File tree

2 files changed

+123
-168
lines changed
Filter options

2 files changed

+123
-168
lines changed

‎Lib/platform.py

Copy file name to clipboardExpand all lines: Lib/platform.py
+120-168Lines changed: 120 additions & 168 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@
2626
# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
2727
# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
2828
# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
29-
# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter
29+
# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve
30+
# Dower
3031
#
3132
# History:
3233
#
3334
# <see CVS and SVN checkin messages for history>
3435
#
36+
# 1.0.8 - changed Windows support to read version from kernel32.dll
3537
# 1.0.7 - added DEV_NULL
3638
# 1.0.6 - added linux_distribution()
3739
# 1.0.5 - fixed Java support to allow running the module on Jython
@@ -469,189 +471,139 @@ def _syscmd_ver(system='', release='', version='',
469471
version = _norm_version(version)
470472
return system, release, version
471473

472-
def _win32_getvalue(key, name, default=''):
474+
_WIN32_CLIENT_RELEASES = {
475+
(5, 0): "2000",
476+
(5, 1): "XP",
477+
# Strictly, 5.2 client is XP 64-bit, but platform.py historically
478+
# has always called it 2003 Server
479+
(5, 2): "2003Server",
480+
(5, None): "post2003",
481+
482+
(6, 0): "Vista",
483+
(6, 1): "7",
484+
(6, 2): "8",
485+
(6, 3): "8.1",
486+
(6, None): "post8.1",
487+
488+
(10, 0): "10",
489+
(10, None): "post10",
490+
}
473491

474-
""" Read a value for name from the registry key.
492+
# Server release name lookup will default to client names if necessary
493+
_WIN32_SERVER_RELEASES = {
494+
(5, 2): "2003Server",
475495

476-
In case this fails, default is returned.
496+
(6, 0): "2008Server",
497+
(6, 1): "2008ServerR2",
498+
(6, 2): "2012Server",
499+
(6, 3): "2012ServerR2",
500+
(6, None): "post2012ServerR2",
501+
}
477502

478-
"""
479-
try:
480-
# Use win32api if available
481-
from win32api import RegQueryValueEx
482-
except ImportError:
483-
# On Python 2.0 and later, emulate using winreg
484-
import winreg
485-
RegQueryValueEx = winreg.QueryValueEx
486-
try:
487-
return RegQueryValueEx(key, name)
488-
except:
489-
return default
503+
def _get_real_winver(maj, min, build):
504+
if maj < 6 or (maj == 6 and min < 2):
505+
return maj, min, build
506+
507+
from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer,
508+
Structure, WinDLL)
509+
from ctypes.wintypes import DWORD, HANDLE
510+
511+
class VS_FIXEDFILEINFO(Structure):
512+
_fields_ = [
513+
("dwSignature", DWORD),
514+
("dwStrucVersion", DWORD),
515+
("dwFileVersionMS", DWORD),
516+
("dwFileVersionLS", DWORD),
517+
("dwProductVersionMS", DWORD),
518+
("dwProductVersionLS", DWORD),
519+
("dwFileFlagsMask", DWORD),
520+
("dwFileFlags", DWORD),
521+
("dwFileOS", DWORD),
522+
("dwFileType", DWORD),
523+
("dwFileSubtype", DWORD),
524+
("dwFileDateMS", DWORD),
525+
("dwFileDateLS", DWORD),
526+
]
527+
528+
kernel32 = WinDLL('kernel32')
529+
version = WinDLL('version')
530+
531+
# We will immediately double the length up to MAX_PATH, but the
532+
# path may be longer, so we retry until the returned string is
533+
# shorter than our buffer.
534+
name_len = actual_len = 130
535+
while actual_len == name_len:
536+
name_len *= 2
537+
name = create_unicode_buffer(name_len)
538+
actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle),
539+
name, len(name))
540+
if not actual_len:
541+
return maj, min, build
542+
543+
size = version.GetFileVersionInfoSizeW(name, None)
544+
if not size:
545+
return maj, min, build
546+
547+
ver_block = c_buffer(size)
548+
if (not version.GetFileVersionInfoW(name, None, size, ver_block) or
549+
not ver_block):
550+
return maj, min, build
551+
552+
pvi = POINTER(VS_FIXEDFILEINFO)()
553+
if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())):
554+
return maj, min, build
555+
556+
maj = pvi.contents.dwProductVersionMS >> 16
557+
min = pvi.contents.dwProductVersionMS & 0xFFFF
558+
build = pvi.contents.dwProductVersionLS >> 16
559+
560+
return maj, min, build
490561

491562
def win32_ver(release='', version='', csd='', ptype=''):
563+
from sys import getwindowsversion
564+
try:
565+
from winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
566+
except ImportError:
567+
from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
492568

493-
""" Get additional version information from the Windows Registry
494-
and return a tuple (version, csd, ptype) referring to version
495-
number, CSD level (service pack), and OS type (multi/single
496-
processor).
497-
498-
As a hint: ptype returns 'Uniprocessor Free' on single
499-
processor NT machines and 'Multiprocessor Free' on multi
500-
processor machines. The 'Free' refers to the OS version being
501-
free of debugging code. It could also state 'Checked' which
502-
means the OS version uses debugging code, i.e. code that
503-
checks arguments, ranges, etc. (Thomas Heller).
569+
winver = getwindowsversion()
570+
maj, min, build = _get_real_winver(*winver[:3])
571+
version = '{0}.{1}.{2}'.format(maj, min, build)
504572

505-
Note: this function works best with Mark Hammond's win32
506-
package installed, but also on Python 2.3 and later. It
507-
obviously only runs on Win32 compatible platforms.
573+
release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
574+
_WIN32_CLIENT_RELEASES.get((maj, None)) or
575+
release)
508576

509-
"""
510-
# XXX Is there any way to find out the processor type on WinXX ?
511-
# XXX Is win32 available on Windows CE ?
512-
#
513-
# Adapted from code posted by Karl Putland to comp.lang.python.
514-
#
515-
# The mappings between reg. values and release names can be found
516-
# here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp
517-
518-
# Import the needed APIs
519-
try:
520-
from win32api import RegQueryValueEx, RegOpenKeyEx, \
521-
RegCloseKey, GetVersionEx
522-
from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
523-
VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
524-
except ImportError:
525-
# Emulate the win32api module using Python APIs
577+
# getwindowsversion() reflect the compatibility mode Python is
578+
# running under, and so the service pack value is only going to be
579+
# valid if the versions match.
580+
if winver[:2] == (maj, min):
526581
try:
527-
sys.getwindowsversion
582+
csd = 'SP{}'.format(winver.service_pack_major)
528583
except AttributeError:
529-
# No emulation possible, so return the defaults...
530-
return release, version, csd, ptype
531-
else:
532-
# Emulation using winreg (added in Python 2.0) and
533-
# sys.getwindowsversion() (added in Python 2.3)
534-
import winreg
535-
GetVersionEx = sys.getwindowsversion
536-
RegQueryValueEx = winreg.QueryValueEx
537-
RegOpenKeyEx = winreg.OpenKeyEx
538-
RegCloseKey = winreg.CloseKey
539-
HKEY_LOCAL_MACHINE = winreg.HKEY_LOCAL_MACHINE
540-
VER_PLATFORM_WIN32_WINDOWS = 1
541-
VER_PLATFORM_WIN32_NT = 2
542-
VER_NT_WORKSTATION = 1
543-
VER_NT_SERVER = 3
544-
REG_SZ = 1
545-
546-
# Find out the registry key and some general version infos
547-
winver = GetVersionEx()
548-
maj, min, buildno, plat, csd = winver
549-
version = '%i.%i.%i' % (maj, min, buildno & 0xFFFF)
550-
if hasattr(winver, "service_pack"):
551-
if winver.service_pack != "":
552-
csd = 'SP%s' % winver.service_pack_major
553-
else:
554-
if csd[:13] == 'Service Pack ':
555-
csd = 'SP' + csd[13:]
556-
557-
if plat == VER_PLATFORM_WIN32_WINDOWS:
558-
regkey = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
559-
# Try to guess the release name
560-
if maj == 4:
561-
if min == 0:
562-
release = '95'
563-
elif min == 10:
564-
release = '98'
565-
elif min == 90:
566-
release = 'Me'
567-
else:
568-
release = 'postMe'
569-
elif maj == 5:
570-
release = '2000'
571-
572-
elif plat == VER_PLATFORM_WIN32_NT:
573-
regkey = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion'
574-
if maj <= 4:
575-
release = 'NT'
576-
elif maj == 5:
577-
if min == 0:
578-
release = '2000'
579-
elif min == 1:
580-
release = 'XP'
581-
elif min == 2:
582-
release = '2003Server'
583-
else:
584-
release = 'post2003'
585-
elif maj == 6:
586-
if hasattr(winver, "product_type"):
587-
product_type = winver.product_type
588-
else:
589-
product_type = VER_NT_WORKSTATION
590-
# Without an OSVERSIONINFOEX capable sys.getwindowsversion(),
591-
# or help from the registry, we cannot properly identify
592-
# non-workstation versions.
593-
try:
594-
key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
595-
name, type = RegQueryValueEx(key, "ProductName")
596-
# Discard any type that isn't REG_SZ
597-
if type == REG_SZ and name.find("Server") != -1:
598-
product_type = VER_NT_SERVER
599-
except OSError:
600-
# Use default of VER_NT_WORKSTATION
601-
pass
602-
603-
if min == 0:
604-
if product_type == VER_NT_WORKSTATION:
605-
release = 'Vista'
606-
else:
607-
release = '2008Server'
608-
elif min == 1:
609-
if product_type == VER_NT_WORKSTATION:
610-
release = '7'
611-
else:
612-
release = '2008ServerR2'
613-
elif min == 2:
614-
if product_type == VER_NT_WORKSTATION:
615-
release = '8'
616-
else:
617-
release = '2012Server'
618-
else:
619-
release = 'post2012Server'
584+
if csd[:13] == 'Service Pack ':
585+
csd = 'SP' + csd[13:]
620586

621-
else:
622-
if not release:
623-
# E.g. Win3.1 with win32s
624-
release = '%i.%i' % (maj, min)
625-
return release, version, csd, ptype
587+
# VER_NT_SERVER = 3
588+
if getattr(winver, 'product_type', None) == 3:
589+
release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
590+
_WIN32_SERVER_RELEASES.get((maj, None)) or
591+
release)
626592

627-
# Open the registry key
593+
key = None
628594
try:
629-
keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
630-
# Get a value to make sure the key exists...
631-
RegQueryValueEx(keyCurVer, 'SystemRoot')
595+
key = OpenKeyEx(HKEY_LOCAL_MACHINE,
596+
r'SOFTWARE\Microsoft\Windows NT\CurrentVersion')
597+
ptype = QueryValueEx(key, 'CurrentType')[0]
632598
except:
633-
return release, version, csd, ptype
634-
635-
# Parse values
636-
#subversion = _win32_getvalue(keyCurVer,
637-
# 'SubVersionNumber',
638-
# ('',1))[0]
639-
#if subversion:
640-
# release = release + subversion # 95a, 95b, etc.
641-
build = _win32_getvalue(keyCurVer,
642-
'CurrentBuildNumber',
643-
('', 1))[0]
644-
ptype = _win32_getvalue(keyCurVer,
645-
'CurrentType',
646-
(ptype, 1))[0]
647-
648-
# Normalize version
649-
version = _norm_version(version, build)
650-
651-
# Close key
652-
RegCloseKey(keyCurVer)
599+
pass
600+
finally:
601+
if key:
602+
CloseKey(key)
603+
653604
return release, version, csd, ptype
654605

606+
655607
def _mac_ver_xml():
656608
fn = '/System/Library/CoreServices/SystemVersion.plist'
657609
if not os.path.exists(fn):

‎Misc/NEWS

Copy file name to clipboardExpand all lines: Misc/NEWS
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Core and Builtins
1818
Library
1919
-------
2020

21+
- Issue #19143: platform module now reads Windows version from kernel32.dll to
22+
avoid compatibility shims.
23+
2124
- Issue #25092: Fix datetime.strftime() failure when errno was already set to
2225
EINVAL.
2326

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.