-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Make Select/ForEach/Where see dynamic properties #6898
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
Make Select/ForEach/Where see dynamic properties #6898
Conversation
|
About the CodeFactor checks: it looks like most of these are not caused by my commit (although I exacerbated an existing issue--added another small helper class to a file that already contains multiple classes). What is the strategy for dealing with these? Ignore them? Take an IOU to fix them in a separate commit? |
|
CodeFactor issues is optional. Please fix only code you created/changed. |
lzybkr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes look great, just some minor feedback.
It'd be great if you can measure the improvement using dynamic sites.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also test *Prop (should be error with -ExpandProperty, maybe also test without -ExpandProperty.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good ideas; will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was the null test added? The C# operator is can be safely used with null values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
D'oh! Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know you're not a fan of silent errors, but I think consistency here is important - you wouldn't want an error just because the object implemented IDMOP, e.g. if some object didn't in the past, but a new version does, scripts would break when they silently "worked" before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that I can't tell the difference between "it failed because the property does not exist" (let's call this case 1) and "it failed because accessing it actually threw some exception" (let's call that case 2).
Existing PowerShell behavior is different for these two cases: case 1 gets an error (which is possible because the ETS tells us up front if the property exists or not), and case 2 does not.
For non-IDMOP objects, there's no problem; but for IDMOPs, we have the chance to attempt a "blind" access, but the cost is that we must have the same response to both cases (because we cannot distinguish between the two). So we have to make a choice: we can either swallow ALL errors (including "The property 'Blarg' does not exist"), or expose them all.
I felt like it was more important to preserve the behavior of showing "The property 'Blarg' does not exist" (case 1) errors than to suppress "FooException thrown when accessing Bloop property" (case 2) errors. This decision certainly must be influenced by my predisposition against silent errors, but actually I was going for usability: a typo or otherwise trying to access a property that does not exist is a common mistake, and generally easily fixed when presented with the error, and if I had to pick one of "error suppression" or "tell the user s/he goofed", I'd choose the latter.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I can at least add a comment about this)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can make some assumptions though:
- non-dynamic members must exist
- if
GetDynamicMemberNamessays the member exists, trust it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes--in fact, those are exactly the assumptions I am making: if it had shown up in GetDynamicMemberNames, it would show up as a dynamic member, and isBlindDynamicAccess would be false. (And it would be false for normal properties as well, of course.) I'll add more comments about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have any intuition for how often wildcards are used to specify multiple properties, but I know I use them that way for formatting commands.
I'm not sure perf matters much here, but it would be good to understand the impact on something like ps | select *mem*.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For select, the wildcard gets resolved into individual MshExpressionResult objects, so no problems there. So this case here really is for the heterogenous-objects-with-different-matching-names case, which I think is probably a rare enough case not to care too much about (though it will work, of course).
|
Regarding the perf: Ultimately the property access itself is such a small part of what goes on that I found it difficult to find any difference at all. Here's what I did: I added a switch to the code, There was enough variability that I tried to eliminate the effect of GCs (by using I tested I tried adding artificial magnification--doing either the dynamic access or normal access in a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please put the comment on separate line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd move the line to line 50 - it is more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use full names instead of aliases like -EA.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't use "m" prefix - right pattern is _serialNumber.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make it static?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please move to line 135.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please address the comment.
if ((x == null) && (target.BaseObject is System.Dynamic.IDynamicMetaObjectProvider))There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe throw here with user friendly message then in line 413 with standard null exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not going to change this; it's totally unrelated to my change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I leave this open - we should fix this in the PR or later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use C# 7 syntax
if ( ex is MethodException mex ...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is also unrelated to my change (harder to tell for this one, but it's just moved a bit).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the 3 comments - modern editors work well with large files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove the unneeded comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove. We can put the code in the file. Or in HelpCommon module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The module is auto-loaded. Please remove the line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same.
Dynamic (DLR) objects work in some places today, but not others. This change expands that support to ForEach-Object, Where-Object and the family of cmdlets that use MshExpression (Select-Object, etc.). (see the linked issue for the simple repro and explanation: fixes PowerShell#6567) This change addresses both wildcard and non-wildcard cases. In wildcard cases, it uses the existing support of generating PSDynamicMember objects for names returned by GetDynamicMemberNames. In non-wildcard cases, a dynamic property access is attempted whether or not the name shows up in GetDynamicMemberNames, but a truly "blind" access is only attempted if we see that the base object is an IDMOP. If the dynamic access fails, you'll get the same or a similar error experience as before ("The property 'Blarg' cannot be found", or no error at all, depending on the cmdlet and the strict mode setting). The included test coverage includes a stub for the .ForEach operator--once people are happy with this change, I can continue by adding support there. This change should allegedly also have positive perf impact, though in actual perf testing, although it does seem ever-so-slightly faster, I found it difficult to measure much difference at all.
14349eb to
58ebb74
Compare
|
@lzybkr Can you update your review? |
|
Thanks for the reviews, people! @TravisEz13 what is the process for getting this merged? Is there any transparency to how you track proposed merges, or is it a "it happens when it happens" sort of thing? I don't mean to be impatient; I'm just wondering how it works, and I want to make sure there aren't any extra steps I need to do or anything like that. |
|
@jazzdelightsme MSFT team has announced that they is actively working on porting Windows PowerShell modules with minimal activity on GitHub. So now some PRs is waiting reviews from MSFT experts. |
|
Hey @jazzdelightsme, thanks for your contribution and patience! |
58ebb74 to
460beb8
Compare
Did it work (to trigger the extra testing)? |
|
@jazzdelightsme The feature tag did work. |
daxian-dbw
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
@jazzdelightsme I only have one comment about a comment in the code.
I can update the comment for you if you are OK with what I suggest.
|
|
||
| if ((x == null) && (target.BaseObject is System.Dynamic.IDynamicMetaObjectProvider)) | ||
| { | ||
| // We could check if GetDynamicMemberNames includes the name... but |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About this comment, we have already checked GetDynamicMemberNames when reaching here (target.Members includes the dynamic members returned from GetDynamicMemberNames). So maybe we can change comment to: GetDynamicMemberNames does not return this name, but GetDynamicMemberNames is only a hint ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your comment is not quite correct. (Maybe it should be correct and there is another bug; I'm not sure what the intended behavior is.)
If you iterate through .Members you will indeed see the dynamic members. However, if you do .Members[propName], where propName is the name of one of those dynamic members... you will get null back.
Sample output:
PS C:\src\PowerShell> $myObj.PSObject.Members | where Name -eq FooProp
MemberType : Dynamic
Value :
TypeNameOfValue : dynamic
Name : FooProp
IsInstance : True
PS C:\src\PowerShell> $myObj.PSObject.Members['FooProp']
PS C:\src\PowerShell> $myObj.PSObject.Members['FooProp'] -eq $null
True
PS C:\src\PowerShell>I'm not sure if that's a bug or By Design. Because you can't do much with PSDynamicMembers (for instance, the getter/setter just throws), maybe it is intentionally excluded when indexing directly in. But it does seem odd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. That does seem odd. I will look into it tomorrow. Thanks for bringing it up!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because DotNetAdapter.GetMember<T>(object obj, string memberName) doesn't return dynamic members. I think it makes sense to not return dynamic member from this method, and submit #7087 to clarify it in the code comments.
|
@jazzdelightsme Thanks for the great contribution! |
PR Summary
Dynamic (DLR) objects work in some places today, but not others. This
change expands that support to ForEach-Object, Where-Object and the
family of cmdlets that use MshExpression (Select-Object, etc.). (see the
linked issue for the simple repro and explanation: fixes #6567)
This change addresses both wildcard and non-wildcard cases. In wildcard
cases, it uses the existing support of generating PSDynamicMember
objects for names returned by GetDynamicMemberNames.
In non-wildcard cases, a dynamic property access is attempted whether or
not the name shows up in GetDynamicMemberNames, but a truly "blind"
access is only attempted if we see that the base object is an IDMOP. If
the dynamic access fails, you'll get the same or a similar error
experience as before ("The property 'Blarg' cannot be found", or no
error at all, depending on the cmdlet and the strict mode setting).
The included test coverage includes a stub for the .ForEach
operator--once people are happy with this change, I can continue by
adding support there.
This change should allegedly also have positive perf impact, though I
have not tested that assertion.
PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:to the beginning of the title and remove the prefix when the PR is ready.[feature]if the change is significant or affects feature tests