Skip to content
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

Writing to the error stream produces no output in methods of custom classes #9702

Closed
mklement0 opened this issue May 23, 2019 · 14 comments
Closed
Labels
Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered.

Comments

@mklement0
Copy link
Contributor

mklement0 commented May 23, 2019

Note: Only [void]-typed methods do not exhibit the problem demonstrated below.

Steps to reproduce

class Foo { [string] Bar() { Get-Item /NoSuch; return 'hi' } }; [Foo]::new().Bar()

Expected behavior

Get-Item : Cannot find path '/NoSuch' because it does not exist.
...
hi

Actual behavior

hi

That is, the error stream output was quietly suppressed.

However, the error is recorded in the automatic $Error variable.

Update: By contrast, all other streams (warning, verbose, debug, information) are passed through.

Environment data

PowerShell Core 6.2.1
Windows PowerShell v5.1
@mklement0 mklement0 added the Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a label May 23, 2019
@fMichaleczek
Copy link

Write-Error doesn't work inside method, only throw works (this is maybe by design)
class Foo { [string] Bar() { Write-Error "MyError" ; return 'hi' } }; [Foo]::new().Bar()

class Foo { [string] Bar() { throw "MyError" ; return 'hi' } }; [Foo]::new().Bar()

Set ErrorAction to Stop, throws the error ( and you can use try/catch to handle the error)
class Foo { [string] Bar() { Get-Item /NoSuch -ErrorAction Stop ; return 'hi' } }; [Foo]::new().Bar()

class Foo { [string] Bar() { try { Get-Item /NoSuch -ErrorAction Stop } catch { throw "MyError:$_" } ; return 'hi' } }; [Foo]::new().Bar()

@vexx32
Copy link
Collaborator

vexx32 commented May 23, 2019

Indeed, the use of Write-Error inside a class method... the expected outcome isn't clear at all. Classes don't have stream implementations of their own, only input and output for data (and exceptions for errors).

@mklement0
Copy link
Contributor Author

@fMichaleczek: Yes, terminating errors work, because they abort execution of the method, but my concern was about nonterminating errors that you may simply want to pass through - while continuing to execute the method.

@vexx32, I naively thought that given that you call cmdlets inside methods, their nonterminating errors would surface too (e.g., Get-ChildItem /nosuchdir).
Currently, there is no way to to directly surface them from methods.

@SeeminglyScience
Copy link
Collaborator

I'd be more inclined to call it a bug that they leak through when the return type is void tbh. Otherwise the only way you'd be able to suppress them would be redirection or maybe $ErrorActionPreference.

Currently, there is no way to to directly surface them from methods.

I think if you really want a class to emit error records or any other stream, you should pass a PSCmdlet object as an argument or save it to a property.

@mklement0
Copy link
Contributor Author

mklement0 commented May 24, 2019

@SeeminglyScience

So you think custom classes are their own world that deals in return values only that then map onto the success output stream - with no other streams available?

Certainly would require documentation, so that no one expects it to work differently, they way I did (happy to create an issue once we have a shared understanding).

However, the worlds are not separate; the error stream is the only exception:

  • All other streams are passed through (which I think makes sense).
  • Additionally, $Error does reflect non-terminating errors that occur in methods.
PS> class Foo { [string] Bar() { Get-Item /NoSuch; write-warning warning; write-verbose verbose -vb; write-debug -debug debug; write-information -InformationAction continue information;  return 'hi' } }; $Error.Clear(); [Foo]::new().Bar()
WARNING: warning
VERBOSE: verbose
DEBUG: debug
information
hi

@SeeminglyScience
Copy link
Collaborator

SeeminglyScience commented May 24, 2019

So you think custom classes are their own world that deals in return values only that then map onto the success output stream - with no other streams available?

In my opinion, yeah absolutely.

However, the worlds are not separate; the error stream is the only exception:

Well, yes and no. Something that I've never liked about those cmdlets is they work in the context of the ICommandRuntime of their own command processor, not of the caller. Class methods don't have a command processor or ICommandRuntime, their invocation is generated sort of like ScriptBlock.Invoke as opposed to an advanced function processed by the Compiler. That's why those cmdlets still work and why it'd be difficult to "fix".

Just so I'm clear though, I'm not pointing at any of that as proof that it should be one way or another, it's just implementation detail. I don't have any insight on what the PowerShell team intended, this is just my opinion.

@mklement0
Copy link
Contributor Author

That's helpful background information, @SeeminglyScience, thanks.

@SteveL-MSFT, can you shed light on the design intent and suggest a resolution?
Having consistent behavior one way or the other would be helpful.

@bpayette
Copy link
Contributor

@mklement0 The design intent was that PowerShell classes should have semantics equivalent to .NET classes (since they are, in fact, .NET classes.) This means explicit return types, a requirement to use the return statement, variables must be initialized before being used, detected at compile-time, not run time, etc. The goal with classes was to make it possible to write more reliable (and larger) scripts in PowerShell by providing more conventional programming language semantics.

@mklement0
Copy link
Contributor Author

mklement0 commented May 24, 2019

Thanks, @bpayette, but what I want to know more specifically is the design intent with respect to passing the PowerShell output streams through (except the success output stream) when commands are called from inside custom-class methods.

@bpayette
Copy link
Contributor

@mklement0

design intent with respect to passing the PowerShell output streams through

The intent was that streams are not a part of classes semantically speaking. Methods return values and throw errors.

@vexx32
Copy link
Collaborator

vexx32 commented May 28, 2019

A somewhat tangential question, @bpayette, then: what of the other streams? It seems the information stream is handled well enough, and though I'll have to test this evening I am fairly sure I recall verbose and debug streams also behaving as they would outside a class context.

@mklement0
Copy link
Contributor Author

mklement0 commented May 28, 2019

Indeed, @vexx32: as the code in the comment above demonstrates, streams 3 - 6 are passed through.

@bpayette
Copy link
Contributor

@vexx32 Those streams are neither output nor error hence they are not involved in a discussion of output/error semantics.

@mklement0
Copy link
Contributor Author

@bpayette

Those streams are neither output nor error hence they are not involved in a discussion of output/error semantics.

How unfortunate, then, that they are involved in the implementation - which led to their involvement in this discussion.

I guess documenting this half-blending/separation of the worlds is our best option at this point: MicrosoftDocs/PowerShell-Docs#4497

@iSazonov iSazonov added the Resolution-Answered The question is answered. label Jun 28, 2019
xenu added a commit to xenu/psperl that referenced this issue Mar 19, 2020
Write-Error is (almost) a no-op inside methods. It used to work
inside void methods in older versions of PowerShell but it was a
bug, not an intentional feature.

See PowerShell/PowerShell#5331 and PowerShell/PowerShell#9702

Also, "throw" is more correct semantically.
xenu added a commit to xenu/psperl that referenced this issue Mar 19, 2020
Write-Error is (almost) a no-op inside methods. It currently works
inside void methods but it's a bug, not an intentional feature.

See PowerShell/PowerShell#5331 and PowerShell/PowerShell#9702

Also, "throw" is more correct semantically.
genio pushed a commit to StrawberryPerl/psperl that referenced this issue Mar 19, 2020
Write-Error is (almost) a no-op inside methods. It currently works
inside void methods but it's a bug, not an intentional feature.

See PowerShell/PowerShell#5331 and PowerShell/PowerShell#9702

Also, "throw" is more correct semantically.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Question ideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a Resolution-Answered The question is answered.
Projects
None yet
Development

No branches or pull requests

6 participants