Skip to content

Throw error on entering non-existing type in Get-FormatData - #7434

Merged
Ilya (iSazonov) merged 12 commits into
PowerShell:masterfrom
faraazahmad:getformat-error
Aug 10, 2018
Merged

Ilya (iSazonov) merged 12 commits into
PowerShell:masterfrom
faraazahmad:getformat-error

Conversation

@faraazahmad

@faraazahmad Syed Faraaz Ahmad (faraazahmad) commented Aug 2, 2018 •

Copy link
Copy Markdown
Contributor

Fix #4235

PR Summary

Write non-terminating error when Get-FormatData doesn't found a type definition for the provided type,

PR Checklist

@msftclas

Microsoft Contribution License Agreements (msftclas) commented Aug 2, 2018 •

Copy link
Copy Markdown

CLA assistant check
All CLA requirements met.


// write out all the available type definitions
foreach (var pair in typedefs)
// If no Type Definitions are found for _typename throw a terminating error

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove obvious comment.

}
else
{
// write out all the available type definitions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove obvious comment.

var typeNames = pair.Key;

if (writeOldWay)
// All files must have the same extension otherwise throw.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the comment is unneeded. I believe we should remove it. (Or move to the method description).

using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Internal;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetically resort all using-s.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this already sorted?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have other using-s below.

It is minor comment.

@iSazonov

Ilya (iSazonov) commented Aug 2, 2018 •

Copy link
Copy Markdown
Collaborator

Seems we could use TypeLoadException

@faraazahmad

Syed Faraaz Ahmad (faraazahmad) commented Aug 2, 2018 •

Copy link
Copy Markdown
Contributor Author

Some of those obvious comments were already there, so I didn't mess with them.

@iSazonov Ilya (iSazonov) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syed Faraaz Ahmad (@faraazahmad) Thanks for you contribution!

@iSazonov

Copy link
Copy Markdown
Collaborator

Michael Klement (@mklement0) Is the fix good for you?

if (writeOldWay)
ErrorRecord errorRecord = new ErrorRecord(
new TypeLoadException("No such type could be found"),
"SPECIFIED_TYPE_NOT_FOUND",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the FullQualifiedErrorId, the convention is to use Pascal casing: SpecifiedTypeNotFound.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, Steve Lee (@SteveL-MSFT).

Additionally, it should be a non-terminating error, given that the cmdlet accepts multiple inputs and potentially produces multiple outputs.

Syed Faraaz Ahmad (@faraazahmad): I know I suggested ResourceUnavailable initially, but on further reflection it probably isn't the right category.

We can use Remove-TypeData as a model, which already reports an error (also non-terminating), and uses category InvalidOperation:

PS> Remove-TypeData NoSuchType
Remove-TypeData : Error in TypeData "NoSuchType": The type "NoSuchType" was not found. The type name value must be the full name of the type. Verify the type name and run the command again.
At line:1 char:1
+ Remove-TypeData NoSuchType
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [Remove-TypeData], RuntimeException
+ FullyQualifiedErrorId : TypesDynamicRemoveException,Microsoft.PowerShell.Commands.RemoveTypeDataCommand

As an aside: I'm not sure TypesDynamicRemoveException was a great choice of error ID.

Steve Lee (@SteveL-MSFT), Ilya (@iSazonov), what do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this should be a non-terminating error if multiple are accepted so no need to stop the entire operation. Agree that TypesDynamicRemoveException is a bad error id, but it's a breaking change to change it now as the intent of the FQEI is to make it internet searchable. For symmetry, it makes sense to use InvalidOperation and probably makes more sense for an end user whereas TypeLoadException is more dev-centric.

@iSazonov

Copy link
Copy Markdown
Collaborator

Steve Lee (@SteveL-MSFT) I added new commit to address your comment, please update your review.


if (writeOldWay)
ErrorRecord errorRecord = new ErrorRecord(
new TypeLoadException("No such type could be found"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exception message string should come from the resource file; UtilityCommonStrings.resx is probably the best location.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@iSazonov

Copy link
Copy Markdown
Collaborator

Steve Lee (@SteveL-MSFT) Dan Travison (@dantraMSFT) Please update your review.

<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SpecifiedTypeNotFound" xml:space="preserve">
<value>Cannot find the type.</value>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be something like:

The type name '{0}' could not be found.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@dantraMSFT Dan Travison (dantraMSFT) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than Steve's recommendation about including the type name in the error message string, LGTM

@Jaykul

Copy link
Copy Markdown
Contributor

HANG ON ... This is a breaking change to the function!!!

Does -ErrorAction SilentlyContinue work, or are you now forcing everyone to wrap this in a try/catch block?

@SteveL-MSFT

Copy link
Copy Markdown
Member

Joel Bennett (@Jaykul) this is a non-terminating error

@mklement0

Copy link
Copy Markdown
Contributor

Joel Bennett (@Jaykul):

Yes, for someone who relied on the previous de facto behavior, this would be a breaking change.

That in itself may be reason enough to not make the change - I have no sense of and have made no attempt to assess the real-world impact. What do you think the real-world impact is?

On the other end of the spectrum: any bug fix is a breaking change: what used to break / work nonsensically before now no longer does.

But let us be clear re:

deliberately handled the situation correctly

Correctly in this situation means:

  • inferred from the de facto behavior

  • with no supporting evidence from the documentation

  • despite the behavior being inconsistent with that of other cmdlets.

And, indeed, the same applies to the current behavior of Get-Module: with a non-wildcard argument it should not default to quietly outputting "nothing" ([System.Management.Automation.Internal.AutomationNull]::Value)

or would you accept a PR for Get-Module too?

In that vein: either yes, or document the surprising existing behavior - see #7498

As an aside:

may return multiple entries even without a wildcard.

Unless you're referring to Get-Module's -ListAvailable switch - an explicit opt-in to multiple matches - neither Get-Module nor Get-FormatData return multiple matches with non-wildcard arguments.

@SteveL-MSFT Steve Lee (SteveL-MSFT) added the Review - Committee The PR/Issue needs a review from the PowerShell Committee label Aug 11, 2018
@faraazahmad
Syed Faraaz Ahmad (faraazahmad) deleted the getformat-error branch August 11, 2018 06:19
@faraazahmad
Syed Faraaz Ahmad (faraazahmad) restored the getformat-error branch August 11, 2018 06:20
@Jaykul

Joel Bennett (Jaykul) commented Aug 12, 2018 •

Copy link
Copy Markdown
Contributor

It's ironic that I'm the one arguing this -- I argued rather passionately against the no-output, no-error design pattern years ago in a discussion on the PowerShell MVP mailing list! Although at the time, my argument was specifically against commands that behave differently when you have a simple name, versus a wildcard (thus allowing the possibility of multiple items).

Ultimately I was rather deluged with examples from the broader ecosystem (Exchange, SCSM, etc.) showing examples of commands quietly returning nothing when they could have returned many things. My point is that this is how PowerShell commands behave, in general:

If the command can return MULTIPLE items, then when there is nothing to return, they just return nothing. That's a deliberate design choice.

And yes, you're wrong Michael Klement (@mklement0), when you say that those commands don't return multiple items with simple parameters. Here are some concrete examples.

# There are about *eight* default rules that affect ScriptInfo, for example:
Get-FormatData System.Management.Automation.ScriptInfo

# Users can have multiple versions of the same module imported because of dependencies.
# We can import multiple versions of Pester as an example...
# (most of us have 3.4.0 and a newer one, right?):
Get-Module Pester -ListAvailable | Import-Module
Get-Module Pester

But it's not just those, most of the default Get commands do this if they could return multiple items:

During the first command of your session, there's no history (or, do Clear-History; Get-History)

Get-History

When a folder is empty

mkdir emptyfolder
Get-ChildItem .\emptyfolder

When there's no input:

Get-Unique
# Or even ...
Get-ChildItem .\EmptyFolder\ | Get-Unique
# Or even ...
Get-ChildItem .\EmptyFolder\ | Get-Random

When there are no sessions

Get-PSSession

When there are no commands

Get-Command -Verb Build

When there's no TypeData:

Get-TypeData System.Boolean

When there's no Jobs:

Get-Job

When there's no scheduled jobs

Get-ScheduledJob

When the clipboard is empty, as after KeePass

Set-Clipboard $null 
Get-Clipboard

I trust I don't need to go through and test every Get command to make the point?

@mklement0

Copy link
Copy Markdown
Contributor

Joel Bennett (@Jaykul):

It's ironic that I'm the one arguing this

I hear you, but let me ask you this before I address your examples:

  • Despite your earlier misgivings, do all the behaviors you list now make sense to you?

  • Do you believe that all of them apply directly to the case at hand?

@Jaykul

Joel Bennett (Jaykul) commented Aug 13, 2018 •

Copy link
Copy Markdown
Contributor

They make sense:

  1. They are returning what's there.
  2. They are consistent to the principle that if the command can return multiple items, in any particular case where there is nothing to return, they return that -- without error.

Obviously in a lot of these cases, it's very clear that there's not an error. That is (if you understood the situation), you didn't actually expect there to be any child items, history, jobs, content, sessions, etc...

I think all of them are examples of the rule, and serve to show that this cmdlet was correct without the PR.


For what it's worth:

If we were willing to break the backward compatibility of PowerShell, then I would be happy to argue again this design pattern and try to convince the world that all commands should have the same output behavior regardless of how many items they might output:

  1. Users (and developers) can't always tell when this will be the case.
  2. Although it's a "common" design pattern, it's clearly not well-known or documented.

But in reality, changing this behavior across all the built-in commands would break too much, and there's no way to actually "fix" it because too many commands are outside the control of this team. We should therefore not alter the expectations of design, and thus, should not change commands.

@faraazahmad

Copy link
Copy Markdown
Contributor Author

I have to agree with Joel Bennett (@Jaykul) , this makes sense. For example, if I ls an empty directory, the expected behaviour would be to not print anything, and not throw an error.

@mklement0

Michael Klement (mklement0) commented Aug 14, 2018 •

Copy link
Copy Markdown
Contributor

The issue is not about output behavior, so what I should've said earlier is: unless you explicitly opt in with switches such as -ListAvailable, specifying a literal name targets a specific, single entity (and how many objects are output as a result is incidental).

The issue is that if you're asking for a specific entity by its literal name (as opposed to a wildcard pattern), not finding that entity should be an error.

(By contrast, a wildcard pattern quietly producing no output if no matches are found makes sense (Get-FormatData *NoSuch*), because instead of saying "give me entity x" you're saying "give me whatever entities happen to match this pattern, if any".)

This is how it currently works in most cmdlets, Get-Module and Get-TypeData being the only other exceptions that I'm aware of - and they should be fixed as well, or - if that is considered too breaking a change - their aberrant behavior should be documented; I've created an issue for Get-TypeData as well: #7521

To quote Bruce Payette (@BrucePay) from the Get-Module issue that I've opened:

As a matter of convention, we usually generate an error for a non-wildcard query e.g. get-item nosuchitem returns a error, but get-module nosuchmodule quietly returns nothing.

None of Joel Bennett (@Jaykul)'s other examples therefore apply:

  • Targeting no specific entity (e.g., Get-History) is a different use case: you're asking for existing entities to be enumerated.

  • As for Get-ChildItem .\emptyfolder: the named specific entity is found, and it just so happens that the information about that entity results in no output, but, as stated, that is incidental. What matters is that if the entity is not found, an error is reported: Get-ChildItem NoSuchDir

@mklement0

Copy link
Copy Markdown
Contributor

Steve Lee (@SteveL-MSFT):

If this fix is ultimately kept, I suggest amending the error message in 2 ways:

  • Make it clear that what couldn't be found wasn't a type per se, but that type's formatting data loaded into the current session.

  • Given the need to to always specify the full type name (that is, you cannot omit the System. component and you cannot use type accelerators), the error message should provide a hint to that effect.

Therefore, instead of The type name '{0}' could not be found., perhaps we could say:

No formatting data for type name '{0}' has been loaded into the current session.
Note that type names must be specified exactly as defined and are typically full .NET type names.

@SteveL-MSFT

Copy link
Copy Markdown
Member

@PowerShell/powershell-committee discussed this and we also discussed it in this morning's Community Call. We agreed that the intent is that if a literal is passed and is not found, it should return a non-terminating error. Cmdlets that don't have this behavior have bugs. In cases where it's explicit or implicit filtering, it should return nothing. We also agreed that we should revert this change and revisit making cmdlets consistent in 6.2. Finally, the real issue is that we are missing Test-* cmdlets so users had to rely on current behavior with Get-* cmdlets to see if something exists and we should add those cmdlets in the future.

@SteveL-MSFT Steve Lee (SteveL-MSFT) added Committee-Reviewed PS-Committee has reviewed this and made a decision and removed Review - Committee The PR/Issue needs a review from the PowerShell Committee labels Aug 16, 2018
@Jaykul

Copy link
Copy Markdown
Contributor

I think you're going to break so very many things -- that 6.2 needs to be 7.0 to be semantic

@essentialexch

Copy link
Copy Markdown

Lord have mercy... once you do this, WPS and CPS are completely different languages that just happen to be related. Forget about moving scripts between the two.

@joeyaiello

Copy link
Copy Markdown
Contributor

I heard today in the @PowerShell/powershell-committee Community Call that we're going to revert this and work towards consistency and Test-* cmdlets across the board.

Steve Lee (@SteveL-MSFT) if we agree that's the case, will the revert make RC? GA?

@SteveL-MSFT

Copy link
Copy Markdown
Member

The @PowerShell/powershell-maintainers will be reverting this commit, but that won't happen for RC where we are already in process of release but will make it for GA. Ilya (@iSazonov) if you are available, can you revert this as the other maintainers are working on the release?

Travis Plunk (TravisEz13) added a commit to TravisEz13/PowerShell that referenced this pull request Aug 16, 2018
Travis Plunk (TravisEz13) added a commit that referenced this pull request Aug 16, 2018
…7434)" (#7546)

Revert "Throw error on entering non-existing type in Get-FormatData (#7434)
This reverts commit b754cd8.

Per this comment #7434 (comment)
@iSazonov

Ilya (iSazonov) commented Aug 17, 2018 •

Copy link
Copy Markdown
Collaborator

Steve Lee (@SteveL-MSFT) The commit already reverted by Travis Plunk (@TravisEz13)

Have we a tracking issue to address the PowerShell Committee conclusion about Get-* and Test-* cmdlets.

@SteveL-MSFT

Copy link
Copy Markdown
Member

Ilya (@iSazonov) The discussion is continuing in #7498. I've created #7562 for new Test- cmdlets

@faraazahmad

Syed Faraaz Ahmad (faraazahmad) commented Aug 17, 2018 •

Copy link
Copy Markdown
Contributor Author

Didn't know my first contribution to Powershell could stir up so back and forth 😅. Is it safe to delete the branch?

@SteveL-MSFT

Copy link
Copy Markdown
Member

Syed Faraaz Ahmad (@faraazahmad) I think it's a healthy discussion. Yes, you can delete the branch.

@faraazahmad
Syed Faraaz Ahmad (faraazahmad) deleted the getformat-error branch August 18, 2018 06:38
Thatgfsj (Thatgfsj) pushed a commit to Thatgfsj-contribs/PowerShell that referenced this pull request Aug 6, 2026
…ll#7434)

Write non-terminating error when `Get-FormatData` doesn't found a type definition for the provided type,
Thatgfsj (Thatgfsj) pushed a commit to Thatgfsj-contribs/PowerShell that referenced this pull request Aug 6, 2026
…owerShell#7434)" (PowerShell#7546)

Revert "Throw error on entering non-existing type in Get-FormatData (PowerShell#7434)
This reverts commit 5e916a3.

Per this comment PowerShell#7434 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Committee-Reviewed PS-Committee has reviewed this and made a decision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Get-FormatData quietly ignores non-existing type-name arguments

9 participants