This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: [Windows] correctly sort and remove duplicates in _winapi getenvironment()
Type: behavior Stage: needs patch
Components: Extension Modules, Windows Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: asaka, eryksun, paul.moore, steve.dower, tim.golden, zach.ware
Priority: normal Keywords:

Created on 2021-04-02 04:16 by eryksun, last changed 2022-04-11 14:59 by admin.

Messages (3)
msg390038 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2021-04-02 04:16
getenvironment() in Modules/_winapi.c needs to sort variables in the environment block and remove duplicates case insensitively [1]. 

The sort order used to matter with SetEnvironmentVairableW(). As soon as it reached a name in the environment block that compared greater than the target name, it would insert a new variable. Nowadays, SetEnvironmentVairableW() searches the entire environment block before inserting a new value. Regardless, at the very least, getenvironment() is not well-behaved and not setting the environment in the documented sort order that users, and possibly other programs, expect.

Case-insensitive sorting in Windows uses upper case. The variable names in the mapping can be added to a list and sorted with a key function that's based on LCMapStringEx() [2], with the flag LCMAP_UPPERCASE. Loop over the sorted list to create the environment block. Remove duplicates by skipping a name that compares equal to the previously stored name according to CompareStringOrdinal() [3].

_winapi.LCMapStringEx(src, flags=LCMAP_UPPERCASE, locale=LOCALE_NAME_INVARIANT) could also be used in ntpath.normcase(), which would resolve bpo-42658.

---

[1] https://docs.microsoft.com/en-us/windows/win32/procthread/changing-environment-variables
[2] https://docs.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-lcmapstringex
[3] https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-comparestringordinal
msg415560 - (view) Author: AN Long (asaka) * Date: 2022-03-19 16:48
I have a question, how to determine which name should be stored if they are duplicated with case insensitive?
msg415563 - (view) Author: Eryk Sun (eryksun) * (Python triager) Date: 2022-03-19 18:03
> which name should be stored if they are duplicated with case insensitive?

Ideally os.environ would preserve the original case of the process environment, and os.environ.copy() would return a copy that's also case insensitive. That would prevent most problems with duplicates keys. See msg387676 in bpo-28824, and msg414319 in bpo-15373.

In msg390038 I suggested keeping the first key that's encountered. However, dicts preserve insertion order nowadays, so one could assume that the last one is the one that the caller wants to keep.
History
Date User Action Args
2022-04-11 14:59:43adminsetgithub: 87868
2022-03-19 18:03:11eryksunsetmessages: + msg415563
2022-03-19 16:48:09asakasetnosy: + asaka
messages: + msg415560
2022-02-26 06:06:11eryksunsetversions: + Python 3.11, - Python 3.8
2021-04-02 04:16:14eryksuncreate