-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Implemented ScriptBlock support for ValidateSet #17460
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
Implemented ScriptBlock support for ValidateSet #17460
Conversation
Co-authored-by: Staffan Gustafsson <staffangu@outlook.com>
Co-authored-by: Ilya <darpa@yandex.ru>
Co-authored-by: Ilya <darpa@yandex.ru>
Co-authored-by: Ilya <darpa@yandex.ru>
|
I'm thinking about this, and it appears that we don't get access to other parameters that may be specified which would be the next logical thing you would want to have in the script block. @iSazonov How would you feel about providing the ScriptBlock parameters similar to that of ArgumentCompleter? param ( $commandName, |
|
@dkattan Could you please share demo script? |
Inspired by the sample for ArgumentCompleters: function Test-DynamicValidateSet {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[ValidateSet('Fruits', 'Vegetables')]
$Type,
[Parameter(Mandatory=$true)]
[ValidateSet({
param ( $commandName,
$parameterName,
$commandAst,
$fakeBoundParameters )
$possibleValues = @{
Fruits = @('Apple', 'Orange', 'Banana')
Vegetables = @('Tomato', 'Squash', 'Corn')
}
if ($fakeBoundParameters.ContainsKey('Type')) {
$possibleValues[$fakeBoundParameters.Type]
} else {
$possibleValues.Values | ForEach-Object {$_}
}
})]
$Value
)
}
Test-DynamicValidateSet -Type Fruits -Value
|
Validation based on other parameters would rely on the order that parameters are bound in. I believe that order is currently documented as undefined (or maybe undocumented). |
I think that's why they chose to call it "fakeBoundParameters" in the ArgumentCompleter implementation. It does a pseudo-binding that seems to be best effort, which is good enough for most cases. |
Yeah it's called that because binding hasn't happened yet, it's all based on statically inferred values. Best effort makes perfect sense for argument completion, but for actual binding time validation I don't think we want to surface something that inconsistent. |
This is beginning to make sense. In order for this to work the way I want it to work I'd need to introduce a "DelayedValidation" feature that waits for all the parameters to be bound and then passes in the bound parameters to the ScriptBlock. Then what happens if multiple parameters are awaiting validation? This is getting deep fast. I'm going to revert the parameter change so we can at least get the basic functionality working. If someone needs access to data in the runspace, they can always declare global variables that would be equally available to both ArgumentCompleters and ValidateSet. |
9d38722 to
e903d41
Compare
|
This PR has Quantification details
Why proper sizing of changes matters
Optimal pull request sizes drive a better predictable PR flow as they strike a
What can I do to optimize my changes
How to interpret the change counts in git diff output
Was this comment helpful? 👍 :ok_hand: :thumbsdown: (Email) |
|
@SeeminglyScience @iSazonov I've reverted the change. How are you guys feeling about this PR? |
It is question for WG. The PR is implementing initial proposal from #3744 but the discussion in #3744 was finished with the conclusion that we need a consistency with binary cmdlets and as result we implemented IValidateSetValuesGenerator in #3784. ( Clearly, this is not very convenient to use in scripts. We could discuss this in #8651) |
|
Dumb question, what’s WG? |
Nevermind, I Googled it https://github.com/PowerShell/PowerShell/blob/master/docs/community/working-group-definitions.md
|
|
@vexx32 Could you add this in WG plan? |
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
|
The Engine-WG discussed this yesterday and we've found a few issues with this concept that will not be easily solvable.
When the latter issue is resolved, the language WG should revisit the former and determine if there is a feasible design. |
|
@SeeminglyScience I’d be happy to work on the module context issue. My end game is to create a UX similar to Show-Command but for the web. My project (immy.bot) is very tightly integrated with PowerShell with custom PSHosts that push Write-Progress to progress bars in the interface, for example. We already have static forms where you can create parameters for scripts, but it gets rather clunky when the need for parameter sets arise. Rather than reinvent the dynamic forms wheel, my thought was to simply parse the params block of the script and save parameter values as a hashtable that can splatted on to the script in question and PowerShell can handle the binding. Obviously this can be done without the feature in the PR, however the primary use case for me does require it, I’ll elaborate. My application installs software on computers based on the user you select. Users are dynamically retrieved from AzureAD. This is currently implemented in C#, but to offer maximum flexibility I’d like to move this logic into PowerShell so users can define their own behavior. Now I suppose I could implement dynamic parameters, assuming that works with Show-Command/Get-Command the syntax is just very cumbersome. I’d rather keep everything in a scriptblock and simply call Get-AzureADUser. ANYWAY, if you wouldn’t mind, can you elaborate on the issue with module context? Is it related to how modules imported using InitialSessionState.ImportPSModuleFromPath are not bound by constrained language mode? |
|
I think this requires the implementation of stateless script block. Theoretically it is possible, but in fact it may be difficult to implement, and most importantly lead to negative consequences for PowerShell language as a whole. |
Can you give me an example on what sort of negative consequences this would have on the language as a whole? |
|
Today any scriptblock exists and run only in one runspace. This is how PowerShell works. |
|
It sounds like the concern is that the implementation of a stateless script block (which I’m interpreting as being a purely functional/isolated script block similar to a static method in C#) would violate the one runspace paradigm of PowerShell. I could argue that by bringing up implicit remoting for Windows PowerShell compatibility or PS Remoting in general, but I’d rather try to understand why you think a stateless scriptblock is necessary in the first place? If not for me, for potential future readers who want to fully understand. And also, help me understand the existing problems with Modules I really am trying to understand this at a deeper level, not trying to be difficult. I appreciate the time you’ve already put into this. |
|
In remoting PowerShell doesn't send scriptblock, it send text. Also there is 1-to-1 mapping local to remote runspace. I don't know what else to add. :-) |
Sorry I think there was a misunderstanding. It just needs the right state, it doesn't need to (and shouldn't) be stateless. @dkattan a quick way to demo what I'm talking about: New-Module {
$ModuleScopedVar = 'one', 'two'
function Test-Command {
[CmdletBinding()]
param(
[ArgumentCompleter({ $script:ModuleScopedVar })]
[string] $Name
)
}
} | Import-ModuleThere are workarounds but this is already a huge UX issue that comes up often on the discord. Unfortunately it wasn't caught (or maybe the impact wasn't realized) for Edit:
^ that's also a good point, language mode is also unlikely to be propagated. |
|
@SeeminglyScience that example helped tremendously. To better illustrate the problem, I created a variable with the same name with value three, four and it tab completes three,four instead of one,two It seems like the problem is that CompleteInput not taking into consideration that Test-Command is defined in a module and therefore shouldn’t have access to variables in the script scope. If I had to create an issue, it would be “Results of CompleteInput in modules potentially affected by variables defined in parent runspace” I understand now that if we allow ValidateSet to be dynamic and not a constant it would have the same problem. Also, do DynamicParameters have the same problem? I’ll test shortly. I have no issue using DynamicParameters for my use case, but I’m afraid I won’t be able to instantiate the required types in Constrained Language mode. (The scripts in my application all run in Constrained Language mode) I suppose I could inject custom Cmdlets that instantiate these objects from C# as a workaround. |
Yes, you mean rather "scope," I mean "sessionstate". |
It's more that the Curiously,
They do not! Neither does |
|
PR Summary
Implements #8651
Adds constructor to ValidateSetAttribute that accepts a ScriptBlock.
Adds logic that evaluates the ScriptBlock when ValidValues is accessed
Adds logic to Compiler.cs that detects the presence of a ScriptBlock and uses the new constructor
Adds test to TabCompletion.Tests.ps1 to verify functionality
PR Context
I would like to be able to have dynamic parameter values self-contained in a function definition.
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.(which runs in a different PS Host).