Repository navigation
Description
Update: Closed in favor of a new proposal at #11201 (comment).
Generally, if you invoke an advanced function / script with:
-
either: no arguments at all
-
or: only arguments that bind to parameters that aren't explicitly tagged as belonging to an explicit parameter set.
PowerShell makes the automatic __AllParameterSets parameter set the effective one ($PSCmdlet.ParameterSetName)
However, this only works if there are either no explicitly defined parameter sets or there is at most one.
With two or more explicit parameter sets, you're suddenly forced to define a default parameter set via [CmdletBinding(DefaultParameterSetName='...')], or else the invocations described above result in an ambiguous-or-incompatible-parameters-specified error.
This situational requirement is obscure, especially given that there's no logical reason to enforce it.
Update: What I am suggesting comes down to this:
If an advanced function or script doesn't explicitly declare a default parameter set via its [CmdletBinding()] attribute:
-
Always allow invocations without arguments as well as invocations comprising only parameters not explicitly assigned to any parameter set - irrespective of the presence of parameters declared as belonging to one or more explicitly named parameter sets and irrespective of whether any of these parameters are declared as mandatory in their sets.
-
Make
$PSCmdlet.ParameterSetNamecontain__AllParameterSetsin that event.- Specifically, do not try to infer a default parameter set from among the explicit ones for parameter-less or only all-parameter-sets parameter invocations.
Also see this alternative (more breaking) proposal, which instead proposes enforcing designating a default parameter set explicitly, at parse time.
See the example in this comment for how that could be useful in the real world.
Technically, this is a breaking change in the following edge cases, both of which apply only if no default parameter set is specified:
-
(a) If a function/script has only one explicitly named parameter set, that parameter set currently becomes the effective one by default in parameter-less invocations if at least one parameter in that explicit set is declared as mandatory:
- That is, currently the mandatory parameter is prompted for; aside from the fact that this behavior is questionable, there's a real bug associated with it - see A prompt for a mandatory parameter can cause $PSCmdlet.DefaultParameterSetName to reflect the wrong parameter set #11201.
- With the proposed change, this will no longer be the case (but arguably it should never have worked this way): a parameter-less invocation will succeed as-is, and
$PSCmdlet.ParameterSetNamewill contain__AllParameterSets.
-
(b) If a function/script has multiple explicitly named parameter sets only one of which does not comprise mandatory parameters, the latter currently becomes the effective one by default in a parameter-less invocation and in all-parameter-sets-only parameters invocations.
- With the proposed change, this will no longer be the case (but arguably it should never have worked that way): such invocations will then make
$PSCmdlet.ParameterSetNamecontain__AllParameterSets.
- With the proposed change, this will no longer be the case (but arguably it should never have worked that way): such invocations will then make
Example of (a):
function foo {
[CmdletBinding()]
param(
[Parameter(ParameterSetName='ByBar', Mandatory)]
$Bar
)
$PSCmdlet.ParameterSetName
}
# Currently: prompts for -Bar;
# After change: allows parameter-less invocation
foo
Example of (b):
function foo {
[CmdletBinding()]
param(
[Parameter(ParameterSetName='ByBar', Mandatory)]
$Bar,
[Parameter(ParameterSetName='ByBaz')] # non-mandatory
$Baz,
$Bam # all-parameter-sets parameter
)
$PSCmdlet.ParameterSetName
}
# Currently: makes $PSCmdlet.ParameterSetName contain 'ByBaz'
# After change: makes $PSCmdlet.ParameterSetName contain '__AllParameterSets'
foo # ditto for: foo -Bam arg
Steps to reproduce
# Advanced function with 1 explicit parameter set
function foo1 { [CmdletBinding()] param($bar1, [Parameter(ParameterSetName='bar2')] $bar2) 'foo1' }
# Advanced function with 2 explicit parameter sets
function foo2 { [CmdletBinding()] param($bar1, [Parameter(ParameterSetName='bar2')] $bar2, [Parameter(ParameterSetName='bar3')] $bar3) 'foo2' }
# Now invoke the functions without arguments, which should succeed in both cases, due to the
# presence of "untagged" parameter $bar1
{ foo1 } | Should -Not -Throw
{ foo2 } | Should -Not -Throw
Expected behavior
Both tests should succeed.
Actual behavior
The 2nd test fails:
Expected no exception to be thrown, but an exception
"Parameter set cannot be resolved using the specified named parameters.
One or more parameters issued cannot be used together or an insufficient number of parameters were provided." was thrown...
Environment data
PowerShell Core 7.0.0-preview.6
Activity
vexx32 commentedon Nov 21, 2019
I've also run into cases where even specifying the default parameter set will not work, especially if you have a default parameter set with no mandatory parameters. It will revert to prompting for mandatory parameters from any other set.
jhoneill commentedon Nov 21, 2019
What you are seeing is correct behaviour
Says there are two parameter sets.
If -Bar2 is passed you are in set "bar2" ; if -Bar3 is passed you in are "bar3" However both parameters are optional so if neither is present you could still be in either of the two sets.
More experienced authors might write like this.
function foo2 { [CmdletBinding()] param($bar1, [Parameter(ParameterSetName='bar2')] $bar2, [Parameter(ParameterSetName='bar3',Mandatory)] $bar3) $PSCmdlet.ParameterSetName }PowerShell can now infer that if -Bar3 is not passed, it can not be using set "bar3" and it must be in set bar2.
I thought that the advice was 1 parameter in every non-default set should be mandatory which takes away the need to have a default.
There is a complication which I blogged here
https://jamesone111.wordpress.com/2016/11/30/powershell-piped-parameter-peculiarities-and-a-palliative-pattern/
from something I first hit in 2013.
mklement0 commentedon Nov 21, 2019
No, you should still be in the automatic
__AllParameterSetsset that the untagged parameter(s) belong to.There's no reason to force you to artificially designate a default parameter set you're not actually using (which is what you need to do to make the problem go away:
[CmdletBinding(DefaultParameterSet='IShouldNotNeedToDoThis')])jhoneill commentedon Nov 22, 2019
Actually no.
What should happen if I run this with no parameters ?
It knows what the default parameter set is, and that it is missing an mandatory parameter so it prompts for it. It doesn't create an extra parameter set and say you can run without the parameter.
Let's try another one. What should happen here
Should it prompt ? Or should it say "there must be two sets, so we're in
__AllParameterSetsdon't prompt" (it prompts)Should this create a third parameter set to run with no parameters ? Most people would look at this and say the command must run with one parameter or the other, but the function has no way to choose a set; and it would be better to set a default and prompt than get "parameter set cannot be resolved" but that is down to the programmer to write in the correct way.
Next we have only one mandatory parameter.
We can tell from the absence of that parameter that we can't be using that set, and there is only one set left. All its parameters are optional. Using second or twentysecond prevents us using first, and vice versa. Again no extra parameter set is created. No mandatory parameter rules out set "one" so we must be in set two even if no optional parameters from that set are given. That's what I meant about not being able to infer anything from the absence of an optional parameter. Presence of mandatory or optional tells you that a set is selected, Absence of mandatory tells you it isn't selected. even without the optional parameter(s) a set might be the only one left.
With those behaviours it would be odd to say, for the one case where no default is given, and there are >= 2 named sets, and the set can't be determined from the parameters given (or mandatory parameters not given) then an extra parameter set should be added.
You're not forced to. In case you've given you can say either "bar1 must be present in set 1, but bar2 is optional in set 2". Only where you have a or b or c means set1 and d or e or f means set (i.e. you can't make a parameter all in all but one of the set) are forced to name a set for "neither".
mklement0 commentedon Nov 22, 2019
You're missing the premise of this issue: that there are non-mandatory all-parameter-sets parameters.
They alone unambiguously imply the automatic
__AllParameterSetsparameter set, if either no parameters are given or only said non-mandatory all-parameter-sets parameters are given.If this works - and it does:
Then so should this - which doesn't:
Please focus on just this specific case in your reply.
jhoneill commentedon Nov 22, 2019
Michael Klement (@mklement0) please stop being obtuse. There is no reason why the second one should work.
The first one fixes an error in the second and where you have a requirement to call with neither parameter but haven't coded that. What you're saying is that PowerShell shouldn't need that fix, and should guess that there was a requirement to call with neither.
You have shown that it is possible for someone who doesn't understand this to write something which a user can call in a way which makes it impossible for PowerShell to make a selection.
You're asking for a special case which is, if more than one set is made up entirely of non-mandatory parameters, PowerShell should create an additional parameter set. I think there should be a special case where 0/0 isn't a divide by zero error but I'm not going to get that. :-)
If you write
it says "There are two parameter sets 'bar2 and 'bar3' , If the user doesn't specify a parameter for one or the other throw an error" .But
says "There are two parameter sets 'bar2 and 'bar3' , If the user doesn't specify a parameter for bar3 you can assume bar2" So does this
and this
Says there is a third set. and is equivalent to
Write the correct one for the situation.
If you want PowerShell to make guesses. This
Says you must give a name or an ID. The function won't work otherwise.
Should this prompt for name or use the "default" parameter set. ?
What it does is selects by name. But if $name isn't mandatory it selects _allParameterSets. That's the broken behaviour but it doesn't mater . No one should be writing a functions with a single one parameter set.
Demanding that everything else behaves like that case is muddled at best
mklement0 commentedon Nov 22, 2019
On a quick meta note, James O'Neill (@jhoneill):
Please remain from making such ad hominem statements and generally from striking a flippant tone in your comments. It adds nothing to the discussion and serves only to antagonize.
mklement0 commentedon Nov 22, 2019
The point is that there shouldn't be a need to code that, because the desired behavior is implied:
Whenever you have at least one untagged parameter (not explicitly assigned to any parameter set), it is implicitly in the
__AllParameterSetsset.PowerShell selects the
__AllParameterSetsparameter set - reflected in$PSCmdlet.ParameterSetName- by default, if you either specify no arguments or only arguments to parameters that are untagged. (If there are no tagged parameters,__AllParameterSetsis by definition always the selected parameter set.)That's how it works with untagged-parameters-only functions and those with only one explicit parameter set (assigned to one or more additional parameters).
There is no logical reason not to extend that logic to multiple explicit parameter sets: If you specify no parameters or only untagged ones,
__AllParameterSetsis implied.Only one explicitly defined parameter set may be the rarer use case, but it's definitely not pointless.
For instance, say you have one untagged parameter, plus two others that must be specified together: you'll need to put those two in an explicit parameter set (but the untagged one can remain untagged).
Here's an example inspired by this real-world use case (not mine) that caused me to open this issue:
Consider function
Write-Message, which should wrapWrite-Hostas follows:-Messageargument is passed, print that message as-is.-AsErroror-AsWarningswitches is passed, print the (default or explicit) message in a switch-specific color.As you can see, the requirement to specify the
PleaseDontMakeMeDoThisparameter set name is artificial - there's no reason for the code not to work without it, because the automatic__AllParameterSetsdefault parameter set could just as well be used instead of the - otherwise unused -PleaseDontMakeMeDoThisexplicit default parameter set.Often you simply don't care or need to care about what
$PSCmdlet.ParameterSetNamecontains.jhoneill commentedon Nov 22, 2019
The case you are making here is ... whatever. You make plenty of comments this is only one where I think the line of reasoning amounts to "being obtuse". If that felt like a personal attack it wasn't intended that way, and I apologize. Though on the flip side others might call characterizing a comment on your reasoning as a personal attack as "antagonistic". So... shall we both don the metaphorical kid gloves and have a final try ?
No. There is a behavior which you have not coded, but which you would like the software to deduce that you want from something else which you are doing incorrectly.
No. There is no
__AllParameterSetsset as such. That is a name which PowerShell uses when it has to create a fictious set, which only happens when there is one set or no sets defined. There is also a flag of the same name which "says this parameter is in all the sets".Test 1 doesn't have any parameter sets. Where PowerShell needs to give a name to the collection of parameters this "anonymous set" gets the label "__AllParameterSets"
Test2 has a parameter set named one. It doesn't have a set named _AllParameterSets
But if it is possible to select "set one", it must be possible to select "not set one".
Again that label is used for the "anonymous parameter set", but the next case has two named sets. There either you are in set one or you are in set2. There will never be an anonymous set once you get to two sets and beyond.
Doesn't work. If you have this
Running test4 with no parameters will prompt for B and C. You must create a default set to allow only -a (or for no parameters at all). Then C will be demanded if only B is supplied and vice versa.
As warning and as error are wrongly specified. What you have said is you can be in Warn without AsWarning, and you can be in err without AsErr they should be
Which says "You an only be in err if AsError is specified, and only be in warn if asWarning is specified"
Where a set has only one parameter that parameter should be mandatory.
This is like the
Get-Somethingwhich must be given either-IDor-Name.If you want a third case you Should tell powershell there is a third set. But you can cheat, and let one case be the default and not have its parameter as mandatory. (provided you don't inspect the set name ... see below).What you're saying is that by wrongly coding parameters which are required in order to select a set as non-mandatory members of the set, you are sending a signal to PowerShell to assume you want it to create a third set. I'm failing to see a strong case for that.
mklement0 commentedon Nov 22, 2019
James O'Neill (@jhoneill):
On the meta issue:
Thanks for apologizing, but to be clear: "please stop being obtuse" refers to a person's behavior, not to a line of reasoning; it is the very embodiment of an ad hominem remark.
For all intents and purposes, there is, as also demonstrated by your examples; most succinctly:
That it would have been better to name this implicit, default parameter set
DefaultI fully agree with.Making these switches mandatory is neither a logical nor an actual requirement. Their mere presence - if specified - selects their specific parameter set.
If neither is specified, the default parameter set applies - and there is no reason that that default parameter set has to be given a name by the user, especially given that there is no strict reason to use it inside the function.
There currently isn't, but there should be, as demonstrated.
There is no good reason to make the implicit
__AllParameterSetsparameter set go away, justbecause there happens to be more than one explicit one.
jhoneill commentedon Nov 22, 2019
Ad hominem is "playing the man not the ball". Was the remark critical of what you are ? Because saying no speech or behaviour can ever be criticized would be absurd.
As written it says "The parameter set can exist without this parameter". If there is only one parameter in a set it is mandatory but you can get away without saying so. Mostly things will work. Ditto not saying which set should be the default. But it's slippy and will create parameter combinations where PowerShell can't apply logic which will always be right
NO ! in this
There are two parameter sets, there is no default set; Message belongs to both sets.
It's not "made to go away". It's not used as label on an artificial set. If zero sets are defined everything belongs to a phantom set. If sets exist at all there must be two or more and PowerShell will add a second to fix "wrong" code when there is only one.
mklement0 commentedon Nov 22, 2019
It was critical of my behavior, based on your conjecture about it - which has nothing to do with critiquing arguments I made.
It is unhelpful in general and needlessly makes things personal to speculate on what motivation may or may not underlie others' comments. The only thing worth addressing is the content of the comments themselves.
There should be, for the reasons stated. (Of course any untagged parameter belongs to all sets, as always, but that is not the point).
I think we understand (as opposed to subscribe to) the respective positions, and at this point, to use the old saw, I suggest we agree to disagree.
jhoneill commentedon Nov 23, 2019
The behaviour was the making of a case.
You have one very specific scenario where you want the language to be changed to figure out that that statements you wrote did not specify your intent.
You wanted 3 valid combinations of parameters to be valid.
With A but Not B,
With B but Not A
How is PowerShell meant to know that it should create an ad hoc third set for neither ? Take this example, which I will say up front is NOT how it should be written.
Here whatever is in the body of the function will NOT do its job properly without an ID or Name. Maybe it it contains something like this
The author of the code emphatically did not intend this to run with neither parameter.
If that happens it will run apply the changes to any user with a name.
They should have said that the parameters are mandatory, AND if they specified a default set the command would prompt for a value instead of failing with an error. They didn't and this script has been used for years with no ill-effects. People's data getting deleted would be a strong argument for preserving the status quo.
All that you have said to this point says for n valid parameter combinations you only want to give names to n-1 of them, and that you find it to an imposition to give the third one a name (witness the infantile naming you used for that set).
When other cases were pointed out to you your response came across as "don't bother me with other cases, I only care about the code I've written for write-host.". ("Please focus on just this specific case in your reply."). It's quite reasonable to ask someone to stop displaying such an attitude and not to demand breaking change in order to save a few keystrokes an area where they demonstrate a lack understanding. Labelling both as "being obtuse" requires no special hypothesis about the person's state of mind, it's just a shorthand which, perhaps, doesn't work over cultural boundaries.
1 remaining item
jhoneill commentedon Nov 24, 2019
OK, you've resorted to latin (which doesn't impress me having studied it at school), and no reach for a dictionary (which owning several doesn't impress me either), for reasons I can only guess at - if it was to demonstrate linguistic expertise may be could use it for short reply to
A response, when told at length and in detail why a demand is both dangerous to others and bad practice , which takes no account of the explanation and simply repeats the demand without adding supporting matter or addressing the reasons given for turning it down
because I will gladly accept that it should replace "Please stop being obtuse".
The behaviour you want to genralize is only useful in specific case. In other cases it is potentially dangerous.
All your example needs to do is mark one of the two parameters as mandatory. It doesn't even need to set a default set. You demand that PowerShell should be changed so you don't need to put Mandatory against one of yours (Or strictly n-1) of yours. AND authors who rely on present behaviour go back and change scripts which would then become dangerous.
It is . You just need to tell PowerShell and not have it make the assumption because the assumption is not safe.
One person can write the [bad] example I gave, another one can write the [bad] example you gave. PowerShell must make a default assumption, which will favour one of them. The assumption in the original design was to fail safely rather than work dangerously. Not running safe code because you weren't told that you should (which is what you are complaining about) is better as a principle than Running potentially unsafe code because you weren't told you should not.
Written with your intent the author gets a run time error and learns to add a "mandatory" or "defaultset" option. Written with the intent of my example the author deletes data.
However you gave this example.
What this shows is a longer way do
Write-host -fore Red "xx"by instead typingWrite-message -error "xx"In real-world use it would suggest an author who did not understand why one should write to the error or warning streams and not use write host so let's assume it was purely for demo purposes...
You would not use
It would be absurd because those are mutually exclusive values for the same thing.
What the example hasn't recognised (possibly because these cases often "normal" and "unusual" so use a switch) is that it is setting a message "level" which may be in one of three states "Error", "Warning" or "Normal" Instead of making those three values for a single parameter (with a Normal as default), two values are expressed as a parameters which must be in sets to prevent two being given, and there is a need to deduce a the third from the absence of the other two (and the user must deduce there is third state), ideally it should be
This allows the user to supply the message and then tab complete values for the level, to see "normal" is an option and allow it to be selected explicitly or by default, and the code body is clear that there are three cases not just two.
switch ($level) {
"Normal" {Write-Information $msg}
"Error" {Write-Error $msg}
"Waring" {Write-Warning $msg}
}
Good style is not to implement a choice between n possibilities for X, using n-1 switches and an unspecified default. Remove those cases and it comes down to cases like "Do you want your excel data formatted as a table, and if so, and what sort of table but if not would you like a named range instead". There are many ways to specify a table so none of the table parameters can be mandatory.
Should PowerShell make me say the single NamedRange parameter is mandatory, or identify a default set? When the answer is "You have to write your code better" a change is only needed "better" is very onerous. How many times could you have tagged one of those parameters as mandatory which would fix your issue with the time that has gone into this discussion. ?
vexx32 commentedon Nov 24, 2019
Can we please agree drop the personal aspect of this argument? It hasn't furthered this discussion in any way.
To your actual point James O'Neill (@jhoneill) I will simply say that the binder is woefully inadequate in any but very simple circumstances. As soon as you have a number of sets with at least one mandatory parameter each, it is completely impossible to tell the binder that you want to create a set with no mandatory parameters at all.
For an exercise in frustration, I invite you to attempt to add a parameter set to Where-Object that takes no mandatory parameters. The binder alternates between other defined sets, but will completely refuse to recognise a set that doesn't contain any mandatory parameters.
If you can figure it out, let me know; I've been trying for weeks to get that going so I can resolve a different issue.
The binder needs improvement.
That said, I do see potential for confusion with users here if we take this suggestion as it is. From what I've seen, at least, it seems quite common for cmdlets and functions to leave optional parameters in "all sets" when the design is based around a specific number of sets. I can see how this suggestion would tend to break that.
I'm not sure what a better solution would be on the whole, at least not without massively complicating the binder and/or the methods in which parameter sets must be defined by the author of a command.
Given that the binder is a sensitive area, I would be inclined to think that any changes to it would require at minimum a fairly specific and extensive RFC, so I don't think a great deal is likely to come from this issue itself. There are a lot of edge cases I'd like to fix in the binder, in truth, but I think this suggestion is a bit too likely to break existing cmdlet and function designs.
I would, however, definitely appreciate some way to indicate to the binder that I do want
__AllParameterSetsto be a valid choice when working through binding logic. There are definitely cases where it would be significantly simpler to design parameter sets if that was an available option. It would probably have to be an option in the[Cmdlet()]and/or[CmdletBinding()]attribute properties somewhere, I would think.mklement0 commentedon Nov 24, 2019
Fully agreed re the personal aspect, Rain Sallow (/u/ta11ow) (@vexx32) : I wish it had never entered the discussion, and my responses were geared toward eliminating it in the future.
In that spirit, allow me one last comment to address that aspect.
James O'Neill (@jhoneill):
The sole reason I linked the quoted (English) phrase to the dictionary definition of the word obtuse was to eliminate any uncertainty around what that word is commonly understood to mean.
mklement0 commentedon Nov 24, 2019
Rain Sallow (/u/ta11ow) (@vexx32):
I've updated the initial post to clarify that
the behavior should only apply if there's at least one untagged parameter present; in the absence of such, the no-argument invocation should default to the one explicit parameter set, if there's only one; otherwise, explicitly naming a default should be required, as is already the case (except if there's only two and only one of them has a mandatory parameter).To be clear: I'm not proposing that that be changed at all. Untagged parameters should continue to be implicitly part of all parameter sets.
What may be confusing is that
_AllParameterSetsdoes double duty:as a "meta" parameter-set name simply signaling "I belong to all parameter sets, whichever ones may be defined".
as a concrete, implicitly defined parameter set that comprises the untagged parameters only, as well as the no-arguments case (if permitted).
With the above clarification re the logic only applying in the presence of at least one tagged parameter, I don't actually see any existing cases that would break - only additional use cases would be enabled.
vexx32 commentedon Nov 24, 2019
Michael Klement (@mklement0) it is pretty common from what I've seen where the author doesn't want
__AllParameterSetsto be its own set, when they have multiple defined sets.As an example:
https://github.com/vexx32/PSWordCloud/blob/master/Module/src/NewWordCloudCommand.cs
I have several parameters that need to be in all the regular sets, but if you could call that with __AllParameterSets being the active set, that command would break (and currently works as intended).
jhoneill commentedon Nov 24, 2019
Michael Klement (@mklement0) Maybe it's a British usage: like the angle "acute" means "sharp" (and therefore quick-witted etc.) and "obtuse" means the opposite but it is used more in the sense "you're looking at this back-to-front", than "you are an idiot". (Possibly people muddle up 'approaching it obliquely' and "going off at tangent") But if you got the sense of "wantonly stupid" from it that was the never the intent. I think there are bits of this Joel doesn't get (see below) and I've assumed he has a brain the size of a planet :-) My biggest learning from being a teacher is that people learn things in different sequences and you can't conclude someone is ignorant because they don't know X, they may know Y and Z which are "advanced topics".
Rain Sallow (/u/ta11ow) (@vexx32) If someone complains about what I've said I tend to apologise or/and justify it, and that's not always the best personality trait.
No, I have that in multiple places just write
[cmdletbinding(defaultParameterSet="a")]Where "a" is a set not referenced anywhere else.
see https://github.com/dfinke/ImportExcel/blob/master/Send-SQLDataToExcel.ps1
for an example with 4 sets each has at least one mandatory parameter and set is selected by the presence of one parameter and the absence of another. Running it with no parameters drops through with a warning message,
or see https://github.com/dfinke/ImportExcel/blob/master/PivotTable.ps1 where a mandatory parameter - the only one in that set - selects a set and no-parameters-in-either-set selects the other set
Like this
https://gist.github.com/jhoneill/83e263836a12f1ccab1019f3ab0373dd
See what I did above. Create one more set name which only appears as the default set.
mklement0 commentedon Nov 24, 2019
Rain Sallow (/u/ta11ow) (@vexx32), I don't think my proposal would break your use case, because the intent is definitely to retain the existing belongs-to-ALL-parameter-sets-if-untagged logic.
__AllParameterSetsbecoming the effective set would apply if and only if:[CmdletBinding()]That is, in your example, passing just the untagged
-ExcludeWordparameter (hypothetically) would still make$PSCmdlet.ParameterSetNamereflectColorBackground, the explicitly designated default set.I really wish we could rename
__AllParameterSetsto something more sensible likeDefaultin the specific usage as a concrete, implicit default set - but that ship has obviously sailed.mklement0 commentedon Nov 26, 2019
Rain Sallow (/u/ta11ow) (@vexx32),
I've realized that my proposal does break certain edge cases, which are now summarized in the initial post.
I've also gone back to proposing that in the absence of an explicit default parameter set parameter-less invocations should always be allowed (whether all-parameter-sets parameters are also declared or not), because the current behavior is questionable in that respect too - and is buggy: see #11201.
The proposal in the initial post now lays out simple rules that should be easy to document and remember.
I realize that nothing may come of this issue directly, but the initial post can now serve as the basis for an RFC and for assessing potential ramifications.
mklement0 commentedon Nov 29, 2019
Rain Sallow (/u/ta11ow) (@vexx32):
After more discussion in #11201, I'm closing this in favor of a new proposal in #11201 (comment).
Like the proposal in this issue, it won't go anywhere without an RFC.
It would undoubtedly be an unacceptable breaking change, but hope it can get the discussion started to at least make it an optional feature.
The basic idea is to:
''(empty string) as the default to explicitly allow the no-argument and all-parameter-set-parameters-only invocations and have$PSCmdlet.ParameterSetNamethen reflect''for such invocations.[Parameter()]attributes.alx9r commentedon May 12, 2020
Rain Sallow (/u/ta11ow) (@vexx32) I'm trying to interpret this statement. I've been using
__AllParameterSetsto tell the binder that I want to create a set with no mandatory parameters at all:Now I'm wondering whether that only happens to work in that limited case. I think the matter of "creating a set with no mandatory parameters at all" is the crux of #12619.
Edit: As James O'Neill (@jhoneill) suggested, it looks like
DefaultParameterSetname = 'NotUsedForParameters'creates a set with no mandatory parameters. So that behavior doesn't seem to depend on__AllParameterSets, at least in this limited case.Did you mean something different in the statement I quoted? Would the
__AllParameterSets/NotUsedForParameterssolution work for the cases you struggled with?vexx32 commentedon May 12, 2020
Nope. It works in some limited cases, but once you get more than a couple of parameter sets the binder fails to resolve the "nonexistent" / "_AllParameterSets" set at all, and starts demanding you supply one of the parameters for one of the mandatory sets, or outright fails.
alx9r commentedon May 12, 2020
Rain Sallow (/u/ta11ow) (@vexx32) I see now. I confirmed as much in this gist using Where-Object's parameters. Indeed merely naming a non-existent default parameter set does not result in binding with no arguments.
The strange part is this: Merely removing the
Notswitch and parameter set does result in binding with no arguments.I can see now why this matter leads to confusion.
vexx32 commentedon May 12, 2020
Yeah I'd love to refactor Where-Object to be able to handle things like
1..10 | Where-Object -ne 7or just| Where-Object(to filter out nulls) but the parameter binder does not like working like that unfortunately, the number of sets gets into the insane levels on that command & I can't see a way to make the binder work with that sensibly in its current state.mklement0 commentedon May 13, 2020
As an aside, alx9r: Currently, PowerShell doesn't enforce that at least one parameter - whether explicitly or implicitly - be part of the designated default parameter set, so there is no strict need for the
$NoSetparameter in your code; that said, my proposal here - unequivocally a breaking change - would change that.