Fix Select-Object to approve UX and fix return a property named *#2421
Fix Select-Object to approve UX and fix return a property named *#2421daxian-dbw merged 7 commits intoPowerShell:masterfrom
Conversation
|
Hi @iSazonov, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution! The agreement was validated by Microsoft and real humans are currently evaluating your PR. TTYL, MSBOT; |
1. Now ExcludeProperty imply Property = '*' 2. Don't return property named '*'
|
@PowerShellTeam please review the code. |
| if (expressionResults.Count == 0) | ||
| if (expressionResults.Count == 0 && !ex.HasWildCardCharacters) | ||
| { | ||
| //Commented out for bug 1107600 |
There was a problem hiding this comment.
Could you please remove the commented-out code snippet here? It's useless and there is no way to figure out what bug 1107600 is now :)
There was a problem hiding this comment.
And it would be better if you can add some comments here, explaining your purpose (allow Select-Object -Property noexist-name to return a PSObject with property noexist-name, unless noexist-name itself contains wildcards).
I find the implementation of Select-Object is so poorly commented (near to zero comment) that it becomes a pain to read the code. So let's add some when we make changes in this file.
There was a problem hiding this comment.
I generally see little comments throughout the Powershell code and tests and thought it was best practice. I will make more comments.
I think @PowerShellTeam needs to mark this moment in documentation: "make more comments in your code".
There was a problem hiding this comment.
I think @PowerShellTeam needs to mark this moment in documentation: "make more comments in your code".
I absolutely agree! I will update coding-conventions to include this.
| # Issue #2420 | ||
| It "Select-Object with implicit Property = '*' don't return property named '*'"{ | ||
| $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing | ||
| $results.psobject.Properties.Item("*").Name | Should Be $null |
There was a problem hiding this comment.
Item("*").Name
.Name seems not necessary here.
| { | ||
| _exclusionFilter = new MshExpressionFilter(ExcludeProperty); | ||
| if ((Property == null) || (Property.Length == 0)) | ||
| { |
There was a problem hiding this comment.
This will result a breaking change -- the scirpt below runs successfully before this change; but with this change a bunch errors will be thrown out.
$s = Get-Process -Id $pid | Select-Object -ExcludeProperty ProcessName -ExpandProperty ModulesHowever, I don't feel this would break any existing scripts practically. It's documented that -ExcludeProperty has no effect without -Property, so I don't think any existing script would only specify -ExcludeProperty.
Maybe we can analyze the powershell scripts on GitHub to get some data. We can borrow @lzybkr's AnalyzeCurlUsage.ps1.
There was a problem hiding this comment.
1.The current tests do not catch it. I think we should add this in tests.
2.There is no difference between:
$s = Get-Process -Id $pid | Select-Object -ExpandProperty Modules
$s[0] | fl *and
$s = Get-Process -Id $pid | Select-Object -ExcludeProperty FileName -ExpandProperty Modules
$s[0] | fl *i.e. ExpandProperty skip ExcludeProperty. So the change could be corrected without breaking.
3.The cmdlet is very intuitive to the user UX and very controversial:
ExcludeProperty is skipped w/o Property
ExpandProperty skip ExcludeProperty
ExpandProperty with Property throw exception
Compare:
1 | Select-Object
"1" | Select-Object
1 | Select-Object -Property *
"1" | Select-Object -Property *and many more.
And it is a tricky code, and any even slightly change this code can easily break down.
(In general I think this cmdlet should be replaced with a newer, which meets modern requirements to cmdlets.)
There was a problem hiding this comment.
I think you mean that it's very unintuitive 😕
If the intention is (to maintain the current expected behavior) that -ExpandProperty ignores -ExcludeProperty wouldn't it be better to have these switches in separate parameter sets? I'd certainly rather have it be obvious that they can't be used together than to allow them together but ignore one (or worse, produce runtime errors if they're used together)...
There was a problem hiding this comment.
I think you mean that it's very unintuitive 😕
Under "intuitive" I mean that we use the cmdlet 10 years "as is" without many complaints (In other words, users get results they expect).
All completely different if you look on the inner side of the cmdlet. I guess it was one of the first cmdlets of Powershell. Today such a design would be immediately rejected. The code is tricky and any chip change results in multiple test crush.
And we absolutely cannot add parameter sets (and moreover we cannot add new exceptions) because this is breaking change.
As I said above, time to replace this cmdlet to a new one with stringent modern design, better UX and better performance.
There was a problem hiding this comment.
@daxian-dbw I fix breaking change and add your test.
There was a problem hiding this comment.
The conclusion is to take your changes to ProcessExpressionParameter() in your second commit -- always add "*" to -Property when -ExcludeProperty is specified but -Property is not : 70c3038
I think you implemented the pseudo code I listed above to avoid the breaking change, but as we discussed here, that will raise new confusion about how -ExlcudeProperty works. So I believe it's reasonable to take your original changes to ProcessExpressionParameter() to make it simple for users to understand.
There was a problem hiding this comment.
Note that I hadn't actually concluded anything after my discussion w/ @daxian-dbw, but after some of my own experiments, I do agree. The existing behavior is surprising and not useful, anyone potentially hit by the breaking change wasn't getting what they wanted anyway.
There was a problem hiding this comment.
@daxian-dbw @lzybkr Thanks for the clarification and the final conclusion! I'll make the rollback.
| # Issue #2420 | ||
| It "Select-Object with explicit Property = '*' don't return property named '*'"{ | ||
| $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing | ||
| $results.psobject.Properties.Item("*").Name | Should Be $null |
| # Issue #2351 | ||
| It "Select-Object with implicit Property = '*' exclude not single prperty"{ | ||
| $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -ExcludeProperty Param2 | ||
| $results.Param2 | Should Be $null |
There was a problem hiding this comment.
Please fix the indentation here.
| # Issue #2351 | ||
| It "Select-Object with explicit Property = '*' exclude not single prperty"{ | ||
| $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -Property * -ExcludeProperty Param2 | ||
| $results.Param2 | Should Be $null |
There was a problem hiding this comment.
Please fix the indentation here.
|
Having separate parameter sets would be a great idea Sent from my iPad On 9 Oct 2016, at 20:30, Joel Bennett <[email protected]mailto:[email protected]> wrote: @Jaykul commented on this pull request. In src/Microsoft.PowerShell.Commands.Utility/commands/utility/select-object.cshttps://github.com//pull/2421:
I think you mean that it's very unintuitive ? If the intention is (to maintain the current expected behavior) that -ExpandProperty ignores -ExcludeProperty wouldn't it be better to have these switches in separate parameter sets? I'd certainly rather have it be obvious that they can't be used together than to allow them together but ignore one (or worse, produce runtime errors if they're used together)... You are receiving this because you are subscribed to this thread. |
Restore ExpandProperty skip ExcludeProperty Add new test Add new comments Remove old comment
|
@RichardSiddaway Sorry, this is the right decision but it is impossible to add parameter sets w/o breaking change. See my comment above. |
|
|
||
| if (ExcludeProperty != null) | ||
| // ExpandProperty skip processing ExcludeProperty | ||
| if (ExcludeProperty != null && string.IsNullOrEmpty(ExpandProperty)) |
There was a problem hiding this comment.
We cannot ignore -ExcludeProperty just because -ExpandProperty is specified. See the script below:
PS:88> $s = Get-Process -Id $pid | Select-Object -Property Process* -ExpandProperty Modules
PS:89> $s[0].ProcessName
powershell
PS:90> $s[0].ProcessorAffinity
255
PS:91> $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity
-ExpandProperty Modules
PS:92> $p[0].ProcessName
powershell
PS:93> $p[0].ProcessorAffinity
PS:94>
-ExcludeProperty took effect and excluded the property ProcessorAffinity, so only ProcessName got added to each Module object.
I feel it's OK to break the Select-Object -ExcludeProperty value1 -ExpandProperty value2 usage based on my analysis of the usage of Select-Object in GitHub (please see my comment above).
There was a problem hiding this comment.
Ok, waiting team approve above then revert the change.
There was a problem hiding this comment.
While waiting I made refactoring. See comment above.
| } | ||
| } | ||
|
|
||
| // allow Select-Object -Property noexist-name to return a PSObject with property noexist-name, |
There was a problem hiding this comment.
It would be clearer if you add quotes around Select-Object -Property noexist-name.
1. Refactoring ProcessExpressionParameter() to make explicit logic for processing Property, ExcludeProperty and ExpandProperty 2. Add new test 3. Correct comment
b34111c to
e5f6d90
Compare
1. Revert ProcessExpressionParameter() to second commit 2. Remove unneeded test
| } | ||
|
|
||
| # Issue #2351 | ||
| It "Select-Object with implicit Property = '*' exclude not single prperty"{ |
There was a problem hiding this comment.
prperty
A typo, should be 'property'
| } | ||
|
|
||
| # Issue #2351 | ||
| It "Select-Object with explicit Property = '*' exclude not single prperty"{ |
There was a problem hiding this comment.
It's fate :)
Fixed.
| $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules | ||
| $p[0].psobject.Properties.Item("ProcessorAffinity") | Should Be $null | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Can you please add a new line?
|
Thanks @iSazonov for the updates! Once the typos in tests are fixed, the PR will be ready to merge. |
1d06ff1 to
cf8654a
Compare
| if ((Property == null) || (Property.Length == 0)) | ||
| { | ||
| Property = new Object[]{"*"}; | ||
| _propertyMshParameterList = processor.ProcessParameters(Property, invocationContext); } |
There was a problem hiding this comment.
@iSazonov sorry that I missed this one this morning. Can you please fix the indentation of the opening '{' and move the closing '}' to a new line? And please also remove the extra newline at line#348.
|
@daxian-dbw Thanks very much! |
|
@iSazonov Thank you for your patience and the great contribution 👍 |
Close #2351
Close #2420