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

Open
mklement0 opened this issue May 23, 2019 · 13 comments

Comments

Projects
None yet
5 participants
@mklement0
Copy link
Contributor

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
@fMichaleczek

This comment has been minimized.

Copy link

commented May 23, 2019

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor Author

commented May 23, 2019

@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

This comment has been minimized.

Copy link
Contributor

commented May 24, 2019

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

This comment has been minimized.

Copy link
Contributor Author

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor Author

commented May 24, 2019

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

This comment has been minimized.

Copy link

commented May 24, 2019

@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

This comment has been minimized.

Copy link
Contributor Author

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

This comment has been minimized.

Copy link

commented May 28, 2019

@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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor Author

commented May 28, 2019

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

@bpayette

This comment has been minimized.

Copy link

commented May 29, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.