From https://github.com/PowerShell/vscode-powershell/issues/2008.
Related to https://github.com/PowerShell/PowerShell/issues/6850.
When using a RunspacePool (common in hosted and remote contexts), the PSModulePath gets reset whenever a runspace is opened.
This occurs because ModuleIntrinsics sets the module path at constructor time:
as if it were a process-singleton.
Every time a runspace is opened in PowerShell, the whole process gets its PSModulePath reset, which is a problem for things like EditorServices where we use a runspace to run analysis in the background, but users expect to be able to modify the PSModulePath in the IntegratedConsole.
I just ran into this too. Here is a repro:
$userPath = '\some\path'
$ENV:PSModulePath = $ENV:PSModulePath+";$userPath"
$ENV:PSModulePath.Split(';')[-1] -eq $userPath
$runspace = [runspacefactory]::CreateRunspace()
$ENV:PSModulePath.Split(';')[-1] -eq $userPath
$runspace.Open()
$ENV:PSModulePath.Split(';')[-1] -eq $userPath
true
true
true
true
true
false
@rjmholt Do you think I should expect that, in the long term, each runspace will have its own instance of PSModulePath? Or is there some competing interest that favors a single process-wide PSModulePath?
in the long term, each runspace will have its own instance of PSModulePath? Or is there some competing interest that favors a single process-wide PSModulePath?
That's a very good question, and one I think needs more design thought.
My thinking is:
Those are all good points. I'll advocate for each runspace having its own PSModulePath for a bit:
This suggests to me that the process-wide PSModulePath singleton should continue to exist, but access to it from each runspace should be copy-on-write. That way each runspace sees the same, correct, initial value, but mutation from that runspace does not affect other runspaces.
That does sound like the best solution, and would be mimicking PS's own scope behaviour of variables as well, so would generally be intuitively understood reasonably well I think.
The Issue will be resolver with #9957. In short PowerShell Committee conclusion is that PowerShell Core will do not modify $env:PSModulePath.
@isazanov Can you elaborate on how #9957 will resolve this issue? #9957 seems to only contemplate the management of PSModulePath across process boundaries whereas this issue is about the management of PSModulePath across runspace boundaries within a process.
I'm seeing this problem manifest in my implementation of Invoke-Parallel. For others facing this, here is a summary of the least-bad still-a-half-measure workaround I've found:
@alx9r My current plan is to implement a global cache s_psModulePath. We will only read $env:PSModulePath, not reset. I think it will resolve the issue and some others.
There can be an edge case - runspace needs to add/set a private path(s). I guess this is incredible. But if it appears, there are several ways to solve it.
My current plan is to implement a global cache s_psModulePath. We will only read $env:PSModulePath, not reset. I think it will resolve the issue and some others.
@iSazonov OK I think understand now. Thank you.
There can be an edge case - runspace needs to add/set a private path(s). I guess this is incredible. But if it appears, there are several ways to solve it.
I expect that issue will appear. Currently $ENV:PSModulePath is mutable, so any user code could mutate it. I've certainly encountered such code. It will be very easy to unknowingly invoke such code in a multi-threaded runspace scenario. But I suppose that's a different issue from this one. :relaxed:
:tada:This issue was addressed in #11057, which has now been successfully released as v7.0.0-preview.6.:tada:
Handy links:
I'm not sure why this issue was closed in #11057, since the code still indicates that instantiating a new ModuleInstrinsics object resets the PSModulePath:
Does it reset it though? SetModulePath() is likely going to use the existing process scope PSModulePath value, not?
It's refresh PSModulePath based on current PSModulePath and computer and user scope configs.
I see the logic is more nuanced than I gave it credit for.
My worry though is that mutating the module path at all across runspaces creates a hidden race condition.
In the PowerShell extension, we embed PSScriptAnalyzer, which uses a runspace pool behind the scenes (and we can't pull it out). If a user sets the module path in their prompt, we have no way to guarantee that that gets used anywhere before it's tampered with by PowerShell.
Basically having something that constructs a new ModuleInstrinsics object makes PSModulePath unpredictable. On first startup it makes sense that we set the module path, but when a create a new runspace, I don't see any upsides to mutating the module path again. I also feel that we ought to not do it using a complex series of methods in a parameterless constructor.
@rjmholt That you ask can be resolved only if we do not write back to PSModulePath environment variable. I proposed the design and pulled PR but PowerShell Committee conclusion was "write back".
I do think PowerShell should only set the PSModulePath environment variable only once per process. The environment path mutation should probably be done in the static constructor of ModuleInstrinsics, but need to check back to the code to see if the current approach is "by design" somehow.
And after that we get feedback "I changed PSModulePath but PowerShell ignore this."
For me it is the same history as cwd - system and Core have a cwd but every runspace has their own cwd.
PSModulePath is used for module discovering and loading but PowerShell load modules per runspace so it should have $PSModulePath per runspace and use PSModulePath env only at startup and runspace initialization.
@iSazonov What you proposed was a too big breaking change that we cannot afford to accept.
@daxian-dbw I did not understand why this is "a too big breaking change" because no one indicated which applications will not work. It looks like a habit :-).
I see only one real scenario where we could populate PSModulePath env - every time we run subprocess with Start.Process() we can add actual PSModulePath env by ProcessInfo.
This issue has been marked as fixed and has not had any activity for 1 day. It has been closed for housekeeping purposes.
The issue from vscode-powershell repo obviously says that we shouldn't write PSModulePath back, PowerShell must only read PSModulePath but not change.