Open jazzdelightsme opened 1 year ago
If you run the import-module with -verbose you'll see it if the PSD doesn't specify aliases it imports all aliases from the function even if they are declared with scope script.. The workround is use the PSD to manage what's internal and external. But it certainly looks wrong that the scope of the alias is disregarded.
My brain isn't properly switched on yet because I use script:
scoped variables and what's failing in your example - and I thought might be strict mode going too far, but I've tried it and it isn't - looks like code that I use most days. At the moment I can't see what's different between the working and non working...
(Good point about the -AliasesToExport
not being controlled. In our "real world" repro, the aliases to export are specified (and don't include the alias that is getting called). I've amended the repro steps to include -AliasesToExport ''
, and it still repro's.)
I tried working around the "internal aliases visible to the external script" part of the problem by changing my aliases to actual full functions (which just manually forward on to the original targets), but that didn't help--all private functions are visible to the external script. 😭
This is undesirable, because some of our internal functions (in the real-world case) are proxies for commonly-used built-in functions. For example, we proxy Write-Host
--we use the proxy to capture our module's output, but don't want output from other things. So when the external script calls Write-Host
, it gets our internal version; our internal version tries to do its thing, which uses a $script:variable
; and that fails miserably (per this bug).
This made me realize that this is just how scoping works in general in PowerShell. It is kind of "weird", IMO, that I can see the otherwise-internal state of all of my callers (all script and local variables, that are not created with -Option Private
; all functions and aliases, whether exported or not)... but that is the way PowerShell does things.
Which reminded me of a trick I could use to get out of this jam. I've had run-ins with scoping troubles before, and I learned (probably from @lzybkr) that a) modules always "inherit" from the global scope, not the current local scope; and b) you can use the call operator (&
) with a module and execute code (a ScriptBlock) in the context of that module. Putting these two things together: if we replace & $ExternalScript
in the repro with & (New-Module { }) $ExternalScript
, voilà: once the external script is magically running with some other "cousin" (instead of descendant) scope, when calling back in to the ReproMod module, the ReproMod is able to see its $script:variables
as usual, so that works; AND the external script is NOT able to see the module's internal aliases or functions.
So that's the workaround that we will use... but I still claim it seems weird that the ReproMod could ever not be able to see its own $script:variables
, when accessed via the script:
prefix.
Steps to reproduce
We have a module, which uses script-scope variables (
$script:varName
) for "private state" (they are not exported from the module).If a command from our module calls an external script (using the call operator
&
) ("frame A"), and that external script ("frame B") calls a function in our module ("frame C"), the code in our module is unable to see its script-scope variables (in frame C).That alone is a problem, but there is also an inconsistency with aliases: although the script-scope variables seem to have disappeared from view in frame C, script-scope aliases in our module are visible (and in fact are visible in frame B, as if they had been exported from the module, though they have not).
I realize this is probably not a SUPER common calling pattern, but this just bit us in real code, so I am interested in a solution.
Here is a simplification of the problem, which we hit in real production code:
Expected Behavior
When running code in our module, it should always be able to see its own script-scope variables. And the external script should not be able to see the script-scope alias (
MPF1
).Actual Behavior
When called from an external script, our module cannot see its own script-scope variables:
And yet the external script is able to call our script-scope alias (
MPF1
).Error details
Environment data