pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/c31be58da8577ef140e83d4e46502c7bb1eb9abf

97560d244c08.css" /> gh-87868: Sort and remove duplicates in getenvironment() (GH-102731) · python/cpython@c31be58 · GitHub
Skip to content

Commit c31be58

Browse files
aiskAlexWaygoodeendebakpterlend-aasland
authored
gh-87868: Sort and remove duplicates in getenvironment() (GH-102731)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com> Co-authored-by: Pieter Eendebak <pieter.eendebak@gmail.com> Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
1 parent fda901a commit c31be58

File tree

3 files changed

+194
-4
lines changed

3 files changed

+194
-4
lines changed

Lib/test/test_subprocess.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,19 @@ def test_env(self):
791791
stdout, stderr = p.communicate()
792792
self.assertEqual(stdout, b"orange")
793793

794+
@unittest.skipUnless(sys.platform == "win32", "Windows only issue")
795+
def test_win32_duplicate_envs(self):
796+
newenv = os.environ.copy()
797+
newenv["fRUit"] = "cherry"
798+
newenv["fruit"] = "lemon"
799+
newenv["FRUIT"] = "orange"
800+
newenv["frUit"] = "banana"
801+
with subprocess.Popen(["CMD", "/c", "SET", "fruit"],
802+
stdout=subprocess.PIPE,
803+
env=newenv) as p:
804+
stdout, _ = p.communicate()
805+
self.assertEqual(stdout.strip(), b"frUit=banana")
806+
794807
# Windows requires at least the SYSTEMROOT environment variable to start
795808
# Python
796809
@unittest.skipIf(sys.platform == 'win32',
@@ -822,6 +835,17 @@ def is_env_var_to_ignore(n):
822835
if not is_env_var_to_ignore(k)]
823836
self.assertEqual(child_env_names, [])
824837

838+
def test_one_environment_variable(self):
839+
newenv = {'fruit': 'orange'}
840+
cmd = [sys.executable, '-c',
841+
'import sys,os;'
842+
'sys.stdout.write("fruit="+os.getenv("fruit"))']
843+
if sys.platform == "win32":
844+
cmd = ["CMD", "/c", "SET", "fruit"]
845+
with subprocess.Popen(cmd, stdout=subprocess.PIPE, env=newenv) as p:
846+
stdout, _ = p.communicate()
847+
self.assertTrue(stdout.startswith(b"fruit=orange"))
848+
825849
def test_invalid_cmd(self):
826850
# null character in the command name
827851
cmd = sys.executable + '\0'
@@ -862,6 +886,19 @@ def test_invalid_env(self):
862886
stdout, stderr = p.communicate()
863887
self.assertEqual(stdout, b"orange=lemon")
864888

889+
@unittest.skipUnless(sys.platform == "win32", "Windows only issue")
890+
def test_win32_invalid_env(self):
891+
# '=' in the environment variable name
892+
newenv = os.environ.copy()
893+
newenv["FRUIT=VEGETABLE"] = "cabbage"
894+
with self.assertRaises(ValueError):
895+
subprocess.Popen(ZERO_RETURN_CMD, env=newenv)
896+
897+
newenv = os.environ.copy()
898+
newenv["==FRUIT"] = "cabbage"
899+
with self.assertRaises(ValueError):
900+
subprocess.Popen(ZERO_RETURN_CMD, env=newenv)
901+
865902
def test_communicate_stdin(self):
866903
p = subprocess.Popen([sys.executable, "-c",
867904
'import sys;'
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Correctly sort and remove duplicate environment variables in
2+
:py:func:`!_winapi.CreateProcess`.

Modules/_winapi.c

Lines changed: 155 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -774,12 +774,157 @@ gethandle(PyObject* obj, const char* name)
774774
return ret;
775775
}
776776

777+
static PyObject *
778+
sortenvironmentkey(PyObject *module, PyObject *item)
779+
{
780+
return _winapi_LCMapStringEx_impl(NULL, LOCALE_NAME_INVARIANT,
781+
LCMAP_UPPERCASE, item);
782+
}
783+
784+
static PyMethodDef sortenvironmentkey_def = {
785+
"sortenvironmentkey", _PyCFunction_CAST(sortenvironmentkey), METH_O, "",
786+
};
787+
788+
static int
789+
sort_environment_keys(PyObject *keys)
790+
{
791+
PyObject *keyfunc = PyCFunction_New(&sortenvironmentkey_def, NULL);
792+
if (keyfunc == NULL) {
793+
return -1;
794+
}
795+
PyObject *kwnames = Py_BuildValue("(s)", "key");
796+
if (kwnames == NULL) {
797+
Py_DECREF(keyfunc);
798+
return -1;
799+
}
800+
PyObject *args[] = { keys, keyfunc };
801+
PyObject *ret = PyObject_VectorcallMethod(&_Py_ID(sort), args, 1, kwnames);
802+
Py_DECREF(keyfunc);
803+
Py_DECREF(kwnames);
804+
if (ret == NULL) {
805+
return -1;
806+
}
807+
Py_DECREF(ret);
808+
809+
return 0;
810+
}
811+
812+
static int
813+
compare_string_ordinal(PyObject *str1, PyObject *str2, int *result)
814+
{
815+
wchar_t *s1 = PyUnicode_AsWideCharString(str1, NULL);
816+
if (s1 == NULL) {
817+
return -1;
818+
}
819+
wchar_t *s2 = PyUnicode_AsWideCharString(str2, NULL);
820+
if (s2 == NULL) {
821+
PyMem_Free(s1);
822+
return -1;
823+
}
824+
*result = CompareStringOrdinal(s1, -1, s2, -1, TRUE);
825+
PyMem_Free(s1);
826+
PyMem_Free(s2);
827+
return 0;
828+
}
829+
830+
static PyObject *
831+
dedup_environment_keys(PyObject *keys)
832+
{
833+
PyObject *result = PyList_New(0);
834+
if (result == NULL) {
835+
return NULL;
836+
}
837+
838+
// Iterate over the pre-ordered keys, check whether the current key is equal
839+
// to the next key (ignoring case), if different, insert the current value
840+
// into the result list. If they are equal, do nothing because we always
841+
// want to keep the last inserted one.
842+
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(keys); i++) {
843+
PyObject *key = PyList_GET_ITEM(keys, i);
844+
845+
// The last key will always be kept.
846+
if (i + 1 == PyList_GET_SIZE(keys)) {
847+
if (PyList_Append(result, key) < 0) {
848+
Py_DECREF(result);
849+
return NULL;
850+
}
851+
continue;
852+
}
853+
854+
PyObject *next_key = PyList_GET_ITEM(keys, i + 1);
855+
int compare_result;
856+
if (compare_string_ordinal(key, next_key, &compare_result) < 0) {
857+
Py_DECREF(result);
858+
return NULL;
859+
}
860+
if (compare_result == CSTR_EQUAL) {
861+
continue;
862+
}
863+
if (PyList_Append(result, key) < 0) {
864+
Py_DECREF(result);
865+
return NULL;
866+
}
867+
}
868+
869+
return result;
870+
}
871+
872+
static PyObject *
873+
normalize_environment(PyObject *environment)
874+
{
875+
PyObject *keys = PyMapping_Keys(environment);
876+
if (keys == NULL) {
877+
return NULL;
878+
}
879+
880+
if (sort_environment_keys(keys) < 0) {
881+
Py_DECREF(keys);
882+
return NULL;
883+
}
884+
885+
PyObject *normalized_keys = dedup_environment_keys(keys);
886+
Py_DECREF(keys);
887+
if (normalized_keys == NULL) {
888+
return NULL;
889+
}
890+
891+
PyObject *result = PyDict_New();
892+
if (result == NULL) {
893+
Py_DECREF(normalized_keys);
894+
return NULL;
895+
}
896+
897+
for (int i = 0; i < PyList_GET_SIZE(normalized_keys); i++) {
898+
PyObject *key = PyList_GET_ITEM(normalized_keys, i);
899+
PyObject *value = PyObject_GetItem(environment, key);
900+
if (value == NULL) {
901+
Py_DECREF(normalized_keys);
902+
Py_DECREF(result);
903+
return NULL;
904+
}
905+
906+
int ret = PyObject_SetItem(result, key, value);
907+
Py_DECREF(value);
908+
if (ret < 0) {
909+
Py_DECREF(normalized_keys);
910+
Py_DECREF(result);
911+
return NULL;
912+
}
913+
}
914+
915+
Py_DECREF(normalized_keys);
916+
917+
return result;
918+
}
919+
777920
static wchar_t *
778921
getenvironment(PyObject* environment)
779922
{
780923
Py_ssize_t i, envsize, totalsize;
781924
wchar_t *buffer = NULL, *p, *end;
782-
PyObject *keys, *values;
925+
PyObject *normalized_environment = NULL;
926+
PyObject *keys = NULL;
927+
PyObject *values = NULL;
783928

784929
/* convert environment dictionary to windows environment string */
785930
if (! PyMapping_Check(environment)) {
@@ -788,11 +933,16 @@ getenvironment(PyObject* environment)
788933
return NULL;
789934
}
790935

791-
keys = PyMapping_Keys(environment);
792-
if (!keys) {
936+
normalized_environment = normalize_environment(environment);
937+
if (normalize_environment == NULL) {
793938
return NULL;
794939
}
795-
values = PyMapping_Values(environment);
940+
941+
keys = PyMapping_Keys(normalized_environment);
942+
if (!keys) {
943+
goto error;
944+
}
945+
values = PyMapping_Values(normalized_environment);
796946
if (!values) {
797947
goto error;
798948
}
@@ -884,6 +1034,7 @@ getenvironment(PyObject* environment)
8841034

8851035
cleanup:
8861036
error:
1037+
Py_XDECREF(normalized_environment);
8871038
Py_XDECREF(keys);
8881039
Py_XDECREF(values);
8891040
return buffer;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy