Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script file directive to select scope during import. #20988

Open
drstonephd opened this issue Jan 2, 2024 · 6 comments
Open

Script file directive to select scope during import. #20988

drstonephd opened this issue Jan 2, 2024 · 6 comments
Labels
Issue-Enhancement the issue is more of a feature request than a bug Needs-Triage The issue is new and needs to be triaged by a work group.

Comments

@drstonephd
Copy link

Summary of the new feature / enhancement

A script can be loaded into the current scope (dot sourced) or into a new script scope (not dot sourced). The script might not be designed to work correctly with both load methods. There should be a means in script to either use the desired scope or to raise an error if loaded into the incorrect scope (e.g., global or script).

In addition, sometimes a set of scripts need to share a scope that's not the global scope. A module provides a single "script" scope for the module that's shared even if the module spans multiple files. Non-module scripts do not have such a shared, non-global scope. Loading a script into such a scope would be a third choice besides using the global scope or dot sourcing into a script scope.

Proposed technical implementation details (optional)

Add an optional "#RequiresScope" directive to control the scope into which a file loads or can load. A SCOPE parameter will, if required, override the scope that would be selected by the method of the load (dot sourced or not dot sourced). A FORCE directive parameter could control if the desired scope is always selected automatically or if an error is raised if the default scope does not match that required.

#RequiresScope Default - uses the either the current or script scope as if the directive was not present
#RequiresScope Global Force - uses the global scope when loaded
#RequiresScope Current Force - uses the current scope when loaded (always behaves as if dot sourced)
#RequiresScope Script Force - uses a new scope when loaded (always behaves as if not dot source)
#RequiresScope Scripts Force - uses a non-global named scope 'scripts' to be the parent one or more script scopes.

The "Scripts" scope would have global as a parent. All scripts added using the "Scripts" directive would be added to this one scope. Basically, it's a special place to dot source scripts other than to global or into another script file's scope. It's sort of like treating one or more non-module scripts as being in a "non-module" module under global.

@drstonephd drstonephd added Issue-Enhancement the issue is more of a feature request than a bug Needs-Triage The issue is new and needs to be triaged by a work group. labels Jan 2, 2024
@drstonephd
Copy link
Author

VSCode would not need an option to load into script source if there was a directive to control such a feature in PS.

PowerShell/vscode-powershell#4327

As a work around for a module not "honoring" preferences, the global scope could be used to control module preferences while a single "Scripts" scope could be used to control user script preferences. Often a user only wants their messages, not noisy messages from modules. For some modules, it can be hard to control messaging without use of global. The result is wrapping a module call with code to set and unset the desired module preferences. This would not be required is all scripts has a scope for just non-module scripts. (I suppose an extra non-global scope for all modules would work also work. It would be a place to define preferences for all modules that can override the global preferences used by non-module scripts.)

#4568

@drstonephd
Copy link
Author

If there is concern about controlling messaging in modules, that is already possible using global preferences. An extra scope between global and all modules does feel like the best way to allow module preferences to be separate from non-module scripts. If so, a new "all modules" scope could be a separate feature isolated from a directive that can force scripts to be correctly loaded into a global, script, or current scope.

@drstonephd
Copy link
Author

I'm not a PowerShell expert. If a module is imported into the global scope, can it still have an extra "all modules" scope between global and the module? The scripts will still be able to access the modules cmdlets?

@rhubarb-geek-nz
Copy link

The script might not be designed to work correctly with both load methods.

Caveat emptor!

Scripts should do one thing and do one thing well and check for errors, but not try to predict or anticipate all the scenarios that it might be used or misused.

The sourcing mechanism is an important way of extending existing scripts, often in ways that are completely valid to the new implementor but not considered by the original script writer. It is the role of the caller to ensure each script is executed in the correct environment with correct arguments.

If you are formally distributing software to third parties, then I recommend it is always packaged as a named and versioned module so you can have the scoping controls that you describe.

@jhoneill
Copy link

jhoneill commented Jan 3, 2024

In addition to what @rhubarb-geek-nz has just said.

The problem comes when someone whose coding is poor (or malicious) demands their script isn't run in it's own scope and pollutes the parent.

As illustration I've met people who put "cls" as the first line of their scripts (part of the problem of "the job of all programs is to print stuff on the console" thinking), they delete the information which I have been keeping in my back-scroll buffer which is not theirs to mess with.

Similarly as "owner" of a scope, I'm the one who decides whether a script gets to mess with it. There are ways to see how a script is invoked and if your script contains a function, and the job of the .PS1 file is to load it is possible to see if you have been dot sourced. A script should run in whatever scope it finds itself in. Grant some things like scripts to add functions will run perfectly, but uselessly if NOT dot sourced, but that's best tested by raising a warning based on $myInvocation when run without a dot.

Not designed to run in its own scope means something makes assignments assuming they will apply to the parent (but not necessarily the global) scope - because what's in the parent is normally readable. That's bad coding.
There are cases where we deliberately access the parent scope (e.g. I have a logging function which checks $MyInvocation from its caller so it knows which script asked for something to be logged) if dot sourced such things won't work properly, but that is down to me to call it the right way.

@rhubarb-geek-nz
Copy link

rhubarb-geek-nz commented Jan 3, 2024

Referring to my comment regarding it is the role of the caller to determine the scope, a common pattern I use in shell scripting is....

for d in $( . /etc/os-release ; echo $ID $ID_LIKE )
{
      echo do something $d
}

So my script is sourcing an operating system script to get it to execute and use its variables, but runs this in a new child process scope so it does not affect the variables in the main process. So my script remains in control, it determines the scope, and /etc/os-release remains a simple script.

So in PowerShell a caller could source any script in a new job to get it to run in a new scope, for example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Enhancement the issue is more of a feature request than a bug Needs-Triage The issue is new and needs to be triaged by a work group.
Projects
None yet
Development

No branches or pull requests

3 participants