You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Questionable internal casing of property name process.env.Path #20605
There is some quirky-ness regarding the property name casing of process.env.PATH (which is actually cased as Path, it seems) and/or the usage of it via the child_process module, e.g. in child_process.spawn.
Does this internal casing of the property name as Path originate from Node.js or is that something that Node is provided by Windows/libuv under the covers? It results in weird behavior when trying to pass in a copied version of process.env to child_process.spawn (etc.).
I don't really understand how the difference is happening here. As far as I could tell looking through the Node.js core code, the env properties are enumerated and turned into envPairs (an array of ${key}=${value} strings), that are then utilized by "process_wrap.cc"'s Spawn method as env_pairs, so I didn't see anywhere where there is an expectation of having an uppercase PATH variable available at all. 😕
That said, how is it that process.env.Path ends up matching on the PATH= check either? The envPairs are generated by enumerating through the process.env keys, which would give it Path rather than PATH as far as I can see.
staticconstenv_var_trequired_vars[] = { /* keep me sorted */
E_V("HOMEDRIVE"),
E_V("HOMEPATH"),
E_V("LOGONSERVER"),
E_V("PATH"),
E_V("SYSTEMDRIVE"),
E_V("SYSTEMROOT"),
E_V("TEMP"),
E_V("USERDOMAIN"),
E_V("USERNAME"),
E_V("USERPROFILE"),
E_V("WINDIR"),
};
so if PATH is missing the OS will be queried (and will get Path, environment variables at the OS level on Windows are case insensitive). It looks like the code will ignore Path in the passed in envPairs (!).
I would agree that seems to be what the code suggests but I also do not have Python in my OS-level PATH variable, so I do think it is somehow utilizing process.env.Path.
So if you set process.env.Path the variable will be set at the OS level. (This doesn't apply if you copy process.env, e.g., let env = { ...process.env }; and set Path on the copy).
C:\work\node\bin\node-v8.11.1-win-x64>node
> process.env.Path;
'C:\\Python27'
> process.env.PATH;
'C:\\Python27'
> process.env.Path='';
''
> process.env.PATH; // for process.env on Windows, Path and PATH are the same (case insensitivity)
''
> var childProcess = require('child_process');
undefined
> let env = { ...process.env };
undefined
> childProcess.spawnSync('python', ['--version'], { env }).stderr.toString().trim()
TypeError: Cannot read property 'toString' of null
> env.Path='C:\\Python27' // libuv ignores this
'C:\\Python27'
> env.PATH // Path and PATH different in the env copy
undefined
> childProcess.spawnSync('python', ['--version'], { env }).stderr.toString().trim()
TypeError: Cannot read property 'toString' of null
> process.env.Path='C:\\Python27'
'C:\\Python27'
> process.env.PATH // for process.env on Windows, Path and PATH are the same (case insensitivity)
'C:\\Python27'
> childProcess.spawnSync('python', ['--version'], { env }).stderr.toString().trim()
'Python 2.7.13'
>
This is kind of a note for myself since I haven't had time to look into this further, but thinking about this a bit more, I'm wondering if the libuv behaviour of using the passed in env.PATH (or env.Path after libuv/libuv#1837) on Windows to find the command to execute is correct. I don't think the Unix platforms behave this way (need to check) and the passed in env is given to the child process but not used to execute the child process?
ping @richardlau should this stay open?
Would note that on Windows the first case-insensitively found path in env (after sorting it) if any will be used to look up the command to execute suffice?
Better documentation is always good and would be good enough to close this out. My last comment should probably still be investigated; if I remember correctly the libuv Windows code was using the path passed through in the env options to look up the command to execute which I don’t think is the case for Unix.
Activity
richardlau commentedon May 8, 2018
I believe the issue is that libuv is looking for uppercase
PATHin the passed inoptions->envin the following code:node/deps/uv/src/win/process.c
Lines 826 to 839 in cf44abb
which is called from the following code to find the path of the application being spawned:
node/deps/uv/src/win/process.c
Lines 1010 to 1046 in cf44abb
cc @nodejs/libuv @nodejs/platform-windows
JamesMGreene commentedon May 8, 2018
Nice find, @richardlau!
JamesMGreene commentedon May 8, 2018
That said, how is it that
process.env.Pathends up matching on thePATH=check either? TheenvPairsare generated by enumerating through theprocess.envkeys, which would give itPathrather thanPATHas far as I can see.richardlau commentedon May 8, 2018
Probably because this function sets any "missing" required environment variables:
node/deps/uv/src/win/process.c
Lines 667 to 824 in cf44abb
of which uppercase
PATHis one:node/deps/uv/src/win/process.c
Lines 48 to 60 in cf44abb
so if
PATHis missing the OS will be queried (and will getPath, environment variables at the OS level on Windows are case insensitive). It looks like the code will ignorePathin the passed inenvPairs(!).JamesMGreene commentedon May 8, 2018
I would agree that seems to be what the code suggests but I also do not have Python in my OS-level
PATHvariable, so I do think it is somehow utilizingprocess.env.Path.richardlau commentedon May 8, 2018
At the OS level
PATHandPathare the same thing.process.envis special in that it has custom getter/setter methods:node/src/node.cc
Lines 2618 to 2647 in b00c34c
node/src/node.cc
Lines 2650 to 2679 in b00c34c
So if you set
process.env.Paththe variable will be set at the OS level. (This doesn't apply if you copyprocess.env, e.g.,let env = { ...process.env };and setPathon the copy).JamesMGreene commentedon May 8, 2018
Ah, that was the missing piece of the puzzle for me. 👌
fix: case-sensitive windows PATH setting for python configuration (#481)
win: perform case insensitive PATH= comparison
richardlau commentedon May 14, 2018
This is kind of a note for myself since I haven't had time to look into this further, but thinking about this a bit more, I'm wondering if the libuv behaviour of using the passed in
env.PATH(orenv.Pathafter libuv/libuv#1837) on Windows to find the command to execute is correct. I don't think the Unix platforms behave this way (need to check) and the passed inenvis given to the child process but not used to execute the child process?lundibundi commentedon Feb 26, 2020
ping @richardlau should this stay open?
Would note that on Windows the first case-insensitively found
pathinenv(after sorting it) if any will be used to look up the command to execute suffice?richardlau commentedon Feb 26, 2020
Better documentation is always good and would be good enough to close this out. My last comment should probably still be investigated; if I remember correctly the libuv Windows code was using the path passed through in the env options to look up the command to execute which I don’t think is the case for Unix.
fix: case-sensitive windows PATH setting for python configuration (ne…
doc: clear up child_process command resolution
doc: clear up child_process command resolution
doc: clear up child_process command resolution
doc: clear up child_process command resolution
doc: clear up child_process command resolution