[Python-checkins] cpython (merge 3.5 -> default): Merge from 3.5 (including all NEWS entries)

steve.dower python-checkins at python.org
Wed Dec 2 12:19:26 EST 2015


https://hg.python.org/cpython/rev/7dcc03b344b5
changeset:   99415:7dcc03b344b5
parent:      99412:1ff29b57628e
parent:      99414:8537ec50c254
user:        Steve Dower <steve.dower at microsoft.com>
date:        Wed Dec 02 09:19:07 2015 -0800
summary:
  Merge from 3.5 (including all NEWS entries)

files:
  Misc/NEWS                                                    |  260 +++++++++-
  Tools/msi/bundle/Default.thm                                 |    2 +-
  Tools/msi/bundle/Default.wxl                                 |    4 +-
  Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp |  223 ++++---
  Tools/msi/bundle/bundle.wxs                                  |    4 +-
  Tools/msi/bundle/packagegroups/launcher.wxs                  |    4 +-
  6 files changed, 389 insertions(+), 108 deletions(-)


diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -476,21 +476,84 @@
   `python3 -m venv`.
 
 
+What's New in Python 3.5.1 final?
+=================================
+
+Release date: 2015-12-06
+
+Core and Builtins
+-----------------
+
+Library
+-------
+
+Windows
+-------
+
+- Issue #25715: Python 3.5.1 installer shows wrong upgrade path and incorrect
+  logic for launcher detection.
+
 What's New in Python 3.5.1 release candidate 1?
 ===============================================
 
-Release date: TBA
+Release date: 2015-11-22
 
 Core and Builtins
 -----------------
 
+- Issue #25630: Fix a possible segfault during argument parsing in functions
+  that accept filesystem paths.
+
+- Issue #23564: Fixed a partially broken sanity check in the _posixsubprocess
+  internals regarding how fds_to_pass were passed to the child.  The bug had
+  no actual impact as subprocess.py already avoided it.
+
+- Issue #25388: Fixed tokenizer crash when processing undecodable source code
+  with a null byte.
+
+- Issue #25462: The hash of the key now is calculated only once in most
+  operations in C implementation of OrderedDict.
+
+- Issue #22995: Default implementation of __reduce__ and __reduce_ex__ now
+  rejects builtin types with not defined __new__.
+
+- Issue #25555: Fix parser and AST: fill lineno and col_offset of "arg" node
+  when compiling AST from Python objects.
+
 - Issue #24802: Avoid buffer overreads when int(), float(), compile(), exec()
   and eval() are passed bytes-like objects.  These objects are not
   necessarily terminated by a null byte, but the functions assumed they were.
 
+- Issue #24726: Fixed a crash and leaking NULL in repr() of OrderedDict that
+  was mutated by direct calls of dict methods.
+
+- Issue #25449: Iterating OrderedDict with keys with unstable hash now raises
+  KeyError in C implementations as well as in Python implementation.
+
+- Issue #25395: Fixed crash when highly nested OrderedDict structures were
+  garbage collected.
+
+- Issue #25274: sys.setrecursionlimit() now raises a RecursionError if the new
+  recursion limit is too low depending at the current recursion depth. Modify
+  also the "lower-water mark" formula to make it monotonic. This mark is used
+  to decide when the overflowed flag of the thread state is reset.
+
 - Issue #24402: Fix input() to prompt to the redirected stdout when
   sys.stdout.fileno() fails.
 
+- Issue #24806: Prevent builtin types that are not allowed to be subclassed from
+  being subclassed through multiple inheritance.
+
+- Issue #24848: Fixed a number of bugs in UTF-7 decoding of misformed data.
+
+- Issue #25280: Import trace messages emitted in verbose (-v) mode are no
+  longer formatted twice.
+
+- Issue #25003: On Solaris 11.3 or newer, os.urandom() now uses the
+  getrandom() function instead of the getentropy() function. The getentropy()
+  function is blocking to generate very good quality entropy, os.urandom()
+  doesn't need such high-quality entropy.
+
 - Issue #25182: The stdprinter (used as sys.stderr before the io module is
   imported at startup) now uses the backslashreplace error handler.
 
@@ -514,6 +577,12 @@
 - Issue #25583: Avoid incorrect errors raised by os.makedirs(exist_ok=True)
   when the OS gives priority to errors such as EACCES over EEXIST.
 
+- Issue #25593: Change semantics of EventLoop.stop() in asyncio.
+
+- Issue #6973: When we know a subprocess.Popen process has died, do
+  not allow the send_signal(), terminate(), or kill() methods to do
+  anything as they could potentially signal a different process.
+
 - Issue #25590: In the Readline completer, only call getattr() once per
   attribute.
 
@@ -521,6 +590,71 @@
   by wrapping a memoryview.  This was a regression made in 3.5a1.  Based
   on patch by Eryksun.
 
+- Issue #25584: Added "escape" to the __all__ list in the glob module.
+
+- Issue #25584: Fixed recursive glob() with patterns starting with '\*\*'.
+
+- Issue #25446: Fix regression in smtplib's AUTH LOGIN support.
+
+- Issue #18010: Fix the pydoc web server's module search function to handle
+  exceptions from importing packages.
+
+- Issue #25554: Got rid of circular references in regular expression parsing.
+
+- Issue #25510: fileinput.FileInput.readline() now returns b'' instead of ''
+  at the end if the FileInput was opened with binary mode.
+  Patch by Ryosuke Ito.
+
+- Issue #25503: Fixed inspect.getdoc() for inherited docstrings of properties.
+  Original patch by John Mark Vandenberg.
+
+- Issue #25515: Always use os.urandom as a source of randomness in uuid.uuid4.
+
+- Issue #21827: Fixed textwrap.dedent() for the case when largest common
+  whitespace is a substring of smallest leading whitespace.
+  Based on patch by Robert Li.
+
+- Issue #25447: The lru_cache() wrapper objects now can be copied and pickled
+  (by returning the original object unchanged).
+
+- Issue #25390: typing: Don't crash on Union[str, Pattern].
+
+- Issue #25441: asyncio: Raise error from drain() when socket is closed.
+
+- Issue #25410: Cleaned up and fixed minor bugs in C implementation of
+  OrderedDict.
+
+- Issue #25411: Improved Unicode support in SMTPHandler through better use of
+  the email package. Thanks to user simon04 for the patch.
+
+- Issue #25407: Remove mentions of the formatter module being removed in
+  Python 3.6.
+
+- Issue #25406: Fixed a bug in C implementation of OrderedDict.move_to_end()
+  that caused segmentation fault or hang in iterating after moving several
+  items to the start of ordered dict.
+
+- Issue #25364: zipfile now works in threads disabled builds.
+
+- Issue #25328: smtpd's SMTPChannel now correctly raises a ValueError if both
+  decode_data and enable_SMTPUTF8 are set to true.
+
+- Issue #25316: distutils raises OSError instead of DistutilsPlatformError
+  when MSVC is not installed.
+
+- Issue #25380: Fixed protocol for the STACK_GLOBAL opcode in
+  pickletools.opcodes.
+
+- Issue #23972: Updates asyncio datagram create method allowing reuseport
+  and reuseaddr socket options to be set prior to binding the socket.
+  Mirroring the existing asyncio create_server method the reuseaddr option
+  for datagram sockets defaults to True if the O/S is 'posix' (except if the
+  platform is Cygwin). Patch by Chris Laws.
+
+- Issue #25304: Add asyncio.run_coroutine_threadsafe().  This lets you
+  submit a coroutine to a loop from another thread, returning a
+  concurrent.futures.Future.  By Vincent Michel.
+
 - Issue #25232: Fix CGIRequestHandler to split the query from the URL at the
   first question mark (?) rather than the last. Patch from Xiang Zhang.
 
@@ -548,6 +682,9 @@
 - Issue #25233: Rewrite the guts of asyncio.Queue and
   asyncio.Semaphore to be more understandable and correct.
 
+- Issue #25203: Failed readline.set_completer_delims() no longer left the
+  module in inconsistent state.
+
 - Issue #23600: Default implementation of tzinfo.fromutc() was returning
   wrong results in some cases.
 
@@ -621,6 +758,19 @@
 - Issue #24881: Fixed setting binary mode in Python implementation of FileIO
   on Windows and Cygwin.  Patch from Akira Li.
 
+- Issue #25578: Fix (another) memory leak in SSLSocket.getpeercer().
+
+- Issue #25530: Disable the vulnerable SSLv3 protocol by default when creating
+  ssl.SSLContext.
+
+- Issue #25569: Fix memory leak in SSLSocket.getpeercert().
+
+- Issue #25471: Sockets returned from accept() shouldn't appear to be
+  nonblocking.
+
+- Issue #25319: When threading.Event is reinitialized, the underlying condition
+  should use a regular lock rather than a recursive lock.
+
 - Issue #21112: Fix regression in unittest.expectedFailure on subclasses.
   Patch from Berker Peksag.
 
@@ -642,8 +792,104 @@
 - Issue #23572: Fixed functools.singledispatch on classes with falsy
   metaclasses.  Patch by Ethan Furman.
 
-- Issue #12006: Add ISO 8601 year, week, and day directives (%G, %V, %u) to
-  strptime.
+- asyncio: ensure_future() now accepts awaitable objects.
+
+IDLE
+----
+
+- Issue 15348: Stop the debugger engine (normally in a user process)
+  before closing the debugger window (running in the IDLE process).
+  This prevents the RuntimeErrors that were being caught and ignored.
+
+- Issue #24455: Prevent IDLE from hanging when a) closing the shell while the
+  debugger is active (15347); b) closing the debugger with the [X] button
+  (15348); and c) activating the debugger when already active (24455).
+  The patch by Mark Roseman does this by making two changes.
+  1. Suspend and resume the gui.interaction method with the tcl vwait
+  mechanism intended for this purpose (instead of root.mainloop & .quit).
+  2. In gui.run, allow any existing interaction to terminate first.
+
+- Change 'The program' to 'Your program' in an IDLE 'kill program?' message
+  to make it clearer that the program referred to is the currently running
+  user program, not IDLE itself.
+
+- Issue #24750: Improve the appearance of the IDLE editor window status bar.
+  Patch by Mark Roseman.
+
+- Issue #25313: Change the handling of new built-in text color themes to better
+  address the compatibility problem introduced by the addition of IDLE Dark.
+  Consistently use the revised idleConf.CurrentTheme everywhere in idlelib.
+
+- Issue #24782: Extension configuration is now a tab in the IDLE Preferences
+  dialog rather than a separate dialog.   The former tabs are now a sorted
+  list.  Patch by Mark Roseman.
+
+- Issue #22726: Re-activate the config dialog help button with some content
+  about the other buttons and the new IDLE Dark theme.
+
+- Issue #24820: IDLE now has an 'IDLE Dark' built-in text color theme.
+  It is more or less IDLE Classic inverted, with a cobalt blue background.
+  Strings, comments, keywords, ... are still green, red, orange, ... .
+  To use it with IDLEs released before November 2015, hit the
+  'Save as New Custom Theme' button and enter a new name,
+  such as 'Custom Dark'.  The custom theme will work with any IDLE
+  release, and can be modified.
+
+- Issue #25224: README.txt is now an idlelib index for IDLE developers and
+  curious users.  The previous user content is now in the IDLE doc chapter.
+  'IDLE' now means 'Integrated Development and Learning Environment'.
+
+- Issue #24820: Users can now set breakpoint colors in
+  Settings -> Custom Highlighting.  Original patch by Mark Roseman.
+
+- Issue #24972: Inactive selection background now matches active selection
+  background, as configured by users, on all systems.  Found items are now
+  always highlighted on Windows.  Initial patch by Mark Roseman.
+
+- Issue #24570: Idle: make calltip and completion boxes appear on Macs
+  affected by a tk regression.  Initial patch by Mark Roseman.
+
+- Issue #24988: Idle ScrolledList context menus (used in debugger)
+  now work on Mac Aqua.  Patch by Mark Roseman.
+
+- Issue #24801: Make right-click for context menu work on Mac Aqua.
+  Patch by Mark Roseman.
+
+- Issue #25173: Associate tkinter messageboxes with a specific widget.
+  For Mac OSX, make them a 'sheet'.  Patch by Mark Roseman.
+
+- Issue #25198: Enhance the initial html viewer now used for Idle Help.
+  * Properly indent fixed-pitch text (patch by Mark Roseman).
+  * Give code snippet a very Sphinx-like light blueish-gray background.
+  * Re-use initial width and height set by users for shell and editor.
+  * When the Table of Contents (TOC) menu is used, put the section header
+  at the top of the screen.
+
+- Issue #25225: Condense and rewrite Idle doc section on text colors.
+
+- Issue #21995: Explain some differences between IDLE and console Python.
+
+- Issue #22820: Explain need for *print* when running file from Idle editor.
+
+- Issue #25224: Doc: augment Idle feature list and no-subprocess section.
+
+- Issue #25219: Update doc for Idle command line options.
+  Some were missing and notes were not correct.
+
+- Issue #24861: Most of idlelib is private and subject to change.
+  Use idleib.idle.* to start Idle. See idlelib.__init__.__doc__.
+
+- Issue #25199: Idle: add synchronization comments for future maintainers.
+
+- Issue #16893: Replace help.txt with help.html for Idle doc display.
+  The new idlelib/help.html is rstripped Doc/build/html/library/idle.html.
+  It looks better than help.txt and will better document Idle as released.
+  The tkinter html viewer that works for this file was written by Mark Roseman.
+  The now unused EditorWindow.HelpDialog class and helt.txt file are deprecated.
+
+- Issue #24199: Deprecate unused idlelib.idlever with possible removal in 3.6.
+
+- Issue #24790: Remove extraneous code (which also create 2 & 3 conflicts).
 
 Documentation
 -------------
@@ -670,6 +916,8 @@
 Tests
 -----
 
+- Issue #25449: Added tests for OrderedDict subclasses.
+
 - Issue #25099: Make test_compileall not fail when an entry on sys.path cannot
   be written to (commonly seen in administrative installs on Windows).
 
@@ -689,7 +937,6 @@
 - Issue #24986: It is now possible to build Python on Windows without errors
   when external libraries are not available.
 
-
 Windows
 -------
 
@@ -728,6 +975,11 @@
 
 - Issue #25022: Removed very outdated PC/example_nt/ directory.
 
+Tools/Demos
+-----------
+
+- Issue #25440: Fix output of python-config --extension-suffix.
+
 
 What's New in Python 3.5.0 final?
 =================================
diff --git a/Tools/msi/bundle/Default.thm b/Tools/msi/bundle/Default.thm
--- a/Tools/msi/bundle/Default.thm
+++ b/Tools/msi/bundle/Default.thm
@@ -66,7 +66,7 @@
 
         <Checkbox Name="Include_launcher" X="185" Y="251" Width="100" Height="24" TabStop="yes" FontId="3" HideWhenDisabled="no">#(loc.Include_launcherLabel)</Checkbox>
         <Checkbox Name="CustomInstallLauncherAllUsers" X="285" Y="251" Width="-11" Height="24" TabStop="yes" FontId="3">#(loc.InstallLauncherAllUsersLabel)</Checkbox>
-        <Text X="205" Y="276" Width="-11" Height="24" TabStop="no" FontId="5">#(loc.Include_launcherHelpLabel)</Text>
+        <Text Name="Include_launcherHelp" X="205" Y="276" Width="-11" Height="24" TabStop="no" FontId="5"></Text>
 
         <Button Name="Custom1BackButton" X="185" Y="-11" Width="85" Height="27" TabStop="yes" FontId="0">#(loc.CustomBackButton)</Button>
         <Button Name="CustomNextButton" X="-101" Y="-11" Width="85" Height="27" TabStop="yes" FontId="0">#(loc.CustomNextButton)</Button>
diff --git a/Tools/msi/bundle/Default.wxl b/Tools/msi/bundle/Default.wxl
--- a/Tools/msi/bundle/Default.wxl
+++ b/Tools/msi/bundle/Default.wxl
@@ -76,7 +76,9 @@
   <String Id="Include_testLabel">Python &test suite</String>
   <String Id="Include_testHelpLabel">Installs the standard library test suite.</String>
   <String Id="Include_launcherLabel">py &launcher</String>
-  <String Id="Include_launcherHelpLabel">Installs the global 'py' launcher to make it easier to start Python.</String>
+  <String Id="Include_launcherHelp">Installs the global 'py' launcher to make it easier to start Python.</String>
+  <String Id="Include_launcherRemove">Use Programs and Features to remove the 'py' launcher.</String>
+  <String Id="Include_launcherUpgrade">Upgrades the global 'py' launcher from the previous version.</String>
   
   <String Id="AssociateFilesLabel">Associate &files with Python (requires the py launcher)</String>
   <String Id="ShortcutsLabel">Create shortcuts for installed applications</String>
diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
--- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
+++ b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
@@ -94,6 +94,7 @@
     ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX,
     ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX,
     ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX,
+    ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL,
     ID_CUSTOM_COMPILE_ALL_CHECKBOX,
     ID_CUSTOM_BROWSE_BUTTON,
     ID_CUSTOM_BROWSE_BUTTON_LABEL,
@@ -158,6 +159,7 @@
     { ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, L"AssociateFiles" },
     { ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX, L"InstallAllUsers" },
     { ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"CustomInstallLauncherAllUsers" },
+    { ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, L"Include_launcherHelp" },
     { ID_CUSTOM_COMPILE_ALL_CHECKBOX, L"CompileAll" },
     { ID_CUSTOM_BROWSE_BUTTON, L"CustomBrowseButton" },
     { ID_CUSTOM_BROWSE_BUTTON_LABEL, L"CustomBrowseButtonLabel" },
@@ -454,6 +456,20 @@
 
         ThemeSendControlMessage(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, BM_SETCHECK,
             installLauncherAllUsers ? BST_CHECKED : BST_UNCHECKED, 0);
+
+        LOC_STRING *pLocString = nullptr;
+        LPCWSTR locKey = L"#(loc.Include_launcherHelp)";
+        LONGLONG detectedLauncher;
+
+        if (SUCCEEDED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher)) && detectedLauncher) {
+            locKey = L"#(loc.Include_launcherRemove)";
+        } else if (SUCCEEDED(BalGetNumericVariable(L"DetectedOldLauncher", &detectedLauncher)) && detectedLauncher) {
+            locKey = L"#(loc.Include_launcherUpgrade)";
+        }
+
+        if (SUCCEEDED(LocGetString(_wixLoc, locKey, &pLocString)) && pLocString) {
+            ThemeSetTextControl(_theme, ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, pLocString->wzText);
+        }
     }
 
     void Custom2Page_Show() {
@@ -641,6 +657,29 @@
         return nResult;
     }
 
+    virtual STDMETHODIMP_(int) OnDetectRelatedMsiPackage(
+        __in_z LPCWSTR wzPackageId,
+        __in_z LPCWSTR /*wzProductCode*/,
+        __in BOOL fPerMachine,
+        __in DWORD64 /*dw64Version*/,
+        __in BOOTSTRAPPER_RELATED_OPERATION operation
+    ) {
+        if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation && 
+            (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1) ||
+             CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1))) {
+            auto hr = LoadAssociateFilesStateFromKey(_engine, fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);
+            if (hr == S_OK) {
+                _engine->SetVariableNumeric(L"AssociateFiles", 1);
+            } else if (FAILED(hr)) {
+                BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
+            }
+
+            _engine->SetVariableNumeric(L"Include_launcher", 1);
+            _engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
+            _engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);
+        }
+        return CheckCanceled() ? IDCANCEL : IDNOACTION;
+    }
 
     virtual STDMETHODIMP_(int) OnDetectRelatedBundle(
         __in LPCWSTR wzBundleId,
@@ -656,24 +695,8 @@
         if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) {
             _downgradingOtherVersion = TRUE;
         } else if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) {
-            _upgradingOldVersion = TRUE;
-
-            // Assume we don't want the launcher or file associations, and if
-            // they have already been installed then loading the state will
-            // reactivate these settings.
-            _engine->SetVariableNumeric(L"Include_launcher", 0);
-            _engine->SetVariableNumeric(L"AssociateFiles", 0);
-            auto hr = LoadLauncherStateFromKey(_engine, HKEY_CURRENT_USER);
-            if (hr == S_FALSE) {
-                hr = LoadLauncherStateFromKey(_engine, HKEY_LOCAL_MACHINE);
-            }
-            if (FAILED(hr)) {
-                BalLog(
-                    BOOTSTRAPPER_LOG_LEVEL_ERROR,
-                    "Failed to load launcher state: error code 0x%08X",
-                    hr
-                );
-            }
+            BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected previous version - planning upgrade");
+            _upgrading = TRUE;
 
             LoadOptionalFeatureStates(_engine);
         } else if (BOOTSTRAPPER_RELATED_OPERATION_NONE == operation) {
@@ -699,9 +722,42 @@
 
     virtual STDMETHODIMP_(void) OnDetectPackageComplete(
         __in LPCWSTR wzPackageId,
-        __in HRESULT /*hrStatus*/,
+        __in HRESULT hrStatus,
         __in BOOTSTRAPPER_PACKAGE_STATE state
-    ) { }
+    ) {
+        if (FAILED(hrStatus)) {
+            return;
+        }
+
+        BOOL detectedLauncher = FALSE;
+        HKEY hkey = HKEY_LOCAL_MACHINE;
+        if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) {
+            if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {
+                detectedLauncher = TRUE;
+                _engine->SetVariableNumeric(L"InstallLauncherAllUsers", 1);
+            }
+        } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1)) {
+            if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {
+                detectedLauncher = TRUE;
+                _engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0);
+            }
+        }
+
+        if (detectedLauncher) {
+            /* When we detect the current version of the launcher. */
+            _engine->SetVariableNumeric(L"Include_launcher", 1);
+            _engine->SetVariableNumeric(L"DetectedLauncher", 1);
+            _engine->SetVariableString(L"Include_launcherState", L"disable");
+            _engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
+
+            auto hr = LoadAssociateFilesStateFromKey(_engine, hkey);
+            if (hr == S_OK) {
+                _engine->SetVariableNumeric(L"AssociateFiles", 1);
+            } else if (FAILED(hr)) {
+                BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
+            }
+        }
+    }
 
 
     virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) {
@@ -716,32 +772,7 @@
 
         if (SUCCEEDED(hrStatus)) {
             // Ensure the default path has been set
-            LONGLONG installAll;
-            LPWSTR targetDir = nullptr;
-            LPWSTR defaultTargetDir = nullptr;
-
-            hrStatus = BalGetStringVariable(L"TargetDir", &targetDir);
-            if (FAILED(hrStatus) || !targetDir || !targetDir[0]) {
-                ReleaseStr(targetDir);
-                targetDir = nullptr;
-
-                if (FAILED(BalGetNumericVariable(L"InstallAllUsers", &installAll))) {
-                    installAll = 0;
-                }
-
-                hrStatus = BalGetStringVariable(
-                    installAll ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",
-                    &defaultTargetDir
-                );
-
-                if (SUCCEEDED(hrStatus) && defaultTargetDir) {
-                    if (defaultTargetDir[0] && SUCCEEDED(BalFormatString(defaultTargetDir, &targetDir))) {
-                        hrStatus = _engine->SetVariableString(L"TargetDir", targetDir);
-                        ReleaseStr(targetDir);
-                    }
-                    ReleaseStr(defaultTargetDir);
-                }
-            }
+            hrStatus = EnsureTargetDir();
         }
 
         SetState(PYBA_STATE_DETECTED, hrStatus);
@@ -1396,9 +1427,14 @@
 
         hr = LoadBootstrapperBAFunctions();
         BalExitOnFailure(hr, "Failed to load bootstrapper functions.");
+
         hr = UpdateUIStrings(_command.action);
         BalExitOnFailure(hr, "Failed to load UI strings.");
 
+        if (_command.action == BOOTSTRAPPER_ACTION_MODIFY) {
+            LoadOptionalFeatureStates(_engine);
+        }
+
         GetBundleFileVersion();
         // don't fail if we couldn't get the version info; best-effort only
     LExit:
@@ -1906,27 +1942,6 @@
         for (DWORD i = 0; i < _theme->cControls; ++i) {
             THEME_CONTROL* pControl = _theme->rgControls + i;
             LPWSTR text = nullptr;
-            LPWSTR name = nullptr;
-            LOC_STRING *locText = nullptr;
-
-            // If a command link has a note, then add it.
-            if ((pControl->dwStyle & BS_TYPEMASK) == BS_COMMANDLINK ||
-                (pControl->dwStyle & BS_TYPEMASK) == BS_DEFCOMMANDLINK) {
-                hr = StrAllocFormatted(&name, L"#(loc.%lsNote)", pControl->sczName);
-                if (SUCCEEDED(hr)) {
-                    hr = LocGetString(_wixLoc, name, &locText);
-                    ReleaseStr(name);
-                    if (SUCCEEDED(hr) && locText && locText->wzText && locText->wzText[0]) {
-                        hr = BalFormatString(locText->wzText, &text);
-                        if (SUCCEEDED(hr) && text && text[0]) {
-                            ThemeSendControlMessage(_theme, pControl->wId, BCM_SETNOTE, 0, (LPARAM)text);
-                            ReleaseStr(text);
-                            text = nullptr;
-                        }
-                    }
-                }
-                hr = S_OK;
-            }
 
             if (!pControl->wPageId && pControl->sczText && *pControl->sczText) {
                 HRESULT hrFormat;
@@ -2048,6 +2063,7 @@
 
         return;
     }
+
     HRESULT UpdateUIStrings(__in BOOTSTRAPPER_ACTION action) {
         HRESULT hr = S_OK;
         LPCWSTR likeInstalling = nullptr;
@@ -2270,6 +2286,30 @@
                     StrFree(controlState);
                 }
                 StrFree(controlName);
+                controlName = nullptr;
+
+
+                // If a command link has a note, then add it.
+                if ((pControl->dwStyle & BS_TYPEMASK) == BS_COMMANDLINK ||
+                    (pControl->dwStyle & BS_TYPEMASK) == BS_DEFCOMMANDLINK) {
+                    hr = StrAllocFormatted(&controlName, L"#(loc.%lsNote)", pControl->sczName);
+                    if (SUCCEEDED(hr)) {
+                        LOC_STRING *locText = nullptr;
+                        hr = LocGetString(_wixLoc, controlName, &locText);
+                        if (SUCCEEDED(hr) && locText && locText->wzText && locText->wzText[0]) {
+                            LPWSTR text = nullptr;
+                            hr = BalFormatString(locText->wzText, &text);
+                            if (SUCCEEDED(hr) && text && text[0]) {
+                                ThemeSendControlMessage(_theme, pControl->wId, BCM_SETNOTE, 0, (LPARAM)text);
+                                ReleaseStr(text);
+                                text = nullptr;
+                            }
+                        }
+                        ReleaseStr(controlName);
+                        controlName = nullptr;
+                    }
+                    hr = S_OK;
+                }
             }
 
             ThemeControlEnable(_theme, pControl->wId, enableControl);
@@ -2515,9 +2555,8 @@
                 if (_installPage == PAGE_LOADING) {
                     switch (_command.action) {
                     case BOOTSTRAPPER_ACTION_INSTALL:
-                        if (_upgradingOldVersion) {
+                        if (_upgrading) {
                             _installPage = PAGE_UPGRADE;
-                            _upgrading = TRUE;
                         } else if (SUCCEEDED(BalGetNumericVariable(L"SimpleInstall", &simple)) && simple) {
                             _installPage = PAGE_SIMPLE_INSTALL;
                         } else {
@@ -2560,11 +2599,9 @@
         static BAL_CONDITION WILL_ELEVATE_CONDITION = {
             L"not WixBundleElevated and ("
                 /*Elevate when installing for all users*/
-                L"InstallAllUsers or"
+                L"InstallAllUsers or "
                 /*Elevate when installing the launcher for all users and it was not detected*/
-                L"(InstallLauncherAllUsers and Include_launcher and not DetectedLauncher) or"
-                /*Elevate when the launcher was installed for all users and it is being removed*/
-                L"(DetectedLauncher and DetectedLauncherAllUsers and not Include_launcher)"
+                L"(Include_launcher and InstallLauncherAllUsers and not DetectedLauncher)"
             L")",
             L""
         };
@@ -2867,19 +2904,16 @@
         return HRESULT_FROM_WIN32(res);
     }
 
-    static HRESULT LoadLauncherStateFromKey(
+    static HRESULT LoadAssociateFilesStateFromKey(
         __in IBootstrapperEngine* pEngine,
         __in HKEY hkHive
     ) {
         const LPCWSTR subkey = L"Software\\Python\\PyLauncher";
         HKEY hKey;
         LRESULT res;
-
-        if (IsTargetPlatformx64(pEngine)) {
-            res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
-        } else {
-            res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
-        }
+        HRESULT hr;
+
+        res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
 
         if (res == ERROR_FILE_NOT_FOUND) {
             return S_FALSE;
@@ -2888,26 +2922,17 @@
             return HRESULT_FROM_WIN32(res);
         }
 
-        res = RegQueryValueExW(hKey, nullptr, nullptr, nullptr, nullptr, nullptr);
-        if (res == ERROR_FILE_NOT_FOUND) {
-            pEngine->SetVariableNumeric(L"Include_launcher", 0);
-        } else if (res == ERROR_SUCCESS) {
-            pEngine->SetVariableNumeric(L"Include_launcher", 1);
-            pEngine->SetVariableNumeric(L"DetectedLauncher", 1);
-            pEngine->SetVariableNumeric(L"InstallLauncherAllUsers", (hkHive == HKEY_LOCAL_MACHINE) ? 1 : 0);
-            pEngine->SetVariableNumeric(L"DetectedLauncherAllUsers", (hkHive == HKEY_LOCAL_MACHINE) ? 1 : 0);
-            pEngine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
-        }
-
         res = RegQueryValueExW(hKey, L"AssociateFiles", nullptr, nullptr, nullptr, nullptr);
         if (res == ERROR_FILE_NOT_FOUND) {
-            pEngine->SetVariableNumeric(L"AssociateFiles", 0);
+            hr = S_FALSE;
         } else if (res == ERROR_SUCCESS) {
-            pEngine->SetVariableNumeric(L"AssociateFiles", 1);
+            hr = S_OK;
+        } else {
+            hr = HRESULT_FROM_WIN32(res);
         }
 
         RegCloseKey(hKey);
-        return S_OK;
+        return hr;
     }
 
     static void LoadOptionalFeatureStates(__in IBootstrapperEngine* pEngine) {
@@ -2918,7 +2943,11 @@
         HKEY hkHive;
 
         // The launcher installation is separate from the Python install, so we
-        // check its state later. This also checks the file association option.
+        // check its state later. For now, assume we don't want the launcher or
+        // file associations, and if they have already been installed then
+        // loading the state will reactivate these settings.
+        pEngine->SetVariableNumeric(L"Include_launcher", 0);
+        pEngine->SetVariableNumeric(L"AssociateFiles", 0);
 
         // Get the registry key from the bundle, to save having to duplicate it
         // in multiple places.
@@ -3089,7 +3118,6 @@
         _hrFinal = hrHostInitialization;
 
         _downgradingOtherVersion = FALSE;
-        _upgradingOldVersion = FALSE;
         _restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
         _restartRequired = FALSE;
         _allowRestart = FALSE;
@@ -3113,8 +3141,6 @@
 
         _hBAFModule = nullptr;
         _baFunction = nullptr;
-
-        EnsureTargetDir();
     }
 
 
@@ -3174,7 +3200,6 @@
     DWORD _calculatedExecuteProgress;
 
     BOOL _downgradingOtherVersion;
-    BOOL _upgradingOldVersion;
     BOOTSTRAPPER_APPLY_RESTART _restartResult;
     BOOL _restartRequired;
     BOOL _allowRestart;
diff --git a/Tools/msi/bundle/bundle.wxs b/Tools/msi/bundle/bundle.wxs
--- a/Tools/msi/bundle/bundle.wxs
+++ b/Tools/msi/bundle/bundle.wxs
@@ -51,7 +51,7 @@
     -->
     <Variable Name="DefaultCustomTargetDir" Value="" bal:Overridable="yes" />
 
-    <Variable Name="InstallAllUsersState" Value="enabled" />
+    <Variable Name="InstallAllUsersState" Value="enabled" bal:Overridable="yes" />
     <?if "$(var.PyTestExt)"="" ?>
     <Variable Name="InstallLauncherAllUsersState" Value="enabled" bal:Overridable="yes" />
     <?else ?>
@@ -72,6 +72,7 @@
     <Variable Name="Include_pip" Value="1" bal:Overridable="yes" />
     <?if "$(var.PyTestExt)"="" ?>
     <Variable Name="Include_launcher" Value="1" bal:Overridable="yes" />
+    <Variable Name="Include_launcherState" Value="enabled" bal:Overridable="yes" />
     <?else ?>
     <Variable Name="Include_launcher" Value="0" />
     <Variable Name="Include_launcherState" Value="disable" />
@@ -81,6 +82,7 @@
     
     <Variable Name="LauncherOnly" Value="0" bal:Overridable="yes" />
     <Variable Name="DetectedLauncher" Value="0" />
+    <Variable Name="DetectedOldLauncher" Value="0" />
     
     <Variable Name="AssociateFiles" Value="1" bal:Overridable="yes" />
     <Variable Name="Shortcuts" Value="1" bal:Overridable="yes" />
diff --git a/Tools/msi/bundle/packagegroups/launcher.wxs b/Tools/msi/bundle/packagegroups/launcher.wxs
--- a/Tools/msi/bundle/packagegroups/launcher.wxs
+++ b/Tools/msi/bundle/packagegroups/launcher.wxs
@@ -11,7 +11,7 @@
                         EnableFeatureSelection="yes"
                         Permanent="yes"
                         Visible="yes"
-                        InstallCondition="(InstallAllUsers or InstallLauncherAllUsers) and Include_launcher" />
+                        InstallCondition="(InstallAllUsers or InstallLauncherAllUsers) and Include_launcher and not DetectedLauncher" />
 
             <MsiPackage Id="launcher_JustForMe"
                         SourceFile="launcher.msi"
@@ -21,7 +21,7 @@
                         EnableFeatureSelection="yes"
                         Permanent="yes"
                         Visible="yes"
-                        InstallCondition="not (InstallAllUsers or InstallLauncherAllUsers) and Include_launcher" />
+                        InstallCondition="not (InstallAllUsers or InstallLauncherAllUsers) and Include_launcher and not DetectedLauncher" />
         </PackageGroup>
     </Fragment>
 </Wix>
\ No newline at end of file

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list