From: Avdi Grimm Date: 2015-12-06T20:42:08+00:00 Subject: [ruby-core:71861] Re: [Ruby trunk - Feature #11588] [Open] Implement structured warnings --001a113f9708c54cd3052640c6ab Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable #3 is a big deal IMHO. Having finer-grained control over which warnings are shown is long overdue. On Tue, Oct 13, 2015 at 11:25 AM wrote: > Issue #11588 has been reported by Daniel Berger. > > ---------------------------------------- > Feature #11588: Implement structured warnings > https://bugs.ruby-lang.org/issues/11588 > > * Author: Daniel Berger > * Status: Open > * Priority: Normal > * Assignee: > ---------------------------------------- > Ruby=E2=80=99s current warning system is lacking. Warnings are controlled= by the > -W flag on the command line, and are generated via the Kernel#warn method > within code. There are a host of problems with this approach to warnings. > > First, warnings aren=E2=80=99t currently testable. With Test::Unit, for e= xample, I > can ensure that specific errors are raised in certain conditions via the > assert_raise method. There is no analogue for warnings. It would be nice = if > there were so I could test them. > > Second, there is no backtrace information provided with warnings. If I > discover a warning I have to wade through the source and figure out where > it was generated, because a Kernel#warn call does not provide a line numb= er > or method name that I can refer back to, unless it happened to be generat= ed > by rb_warn(). For large code bases that can be problematic. > > Third, and most significantly, with warning flags it=E2=80=99s all or not= hing. I > cannot enable or disable specific kinds of warnings. Perl, for example, > implements warning control through pragmas. So, for example, I can specif= y > =E2=80=9Cno warnings uninitialized=E2=80=9D in a Perl program and warning= s about > uninitialized variables go away. With Ruby it=E2=80=99s off, on, or even-= more-on > (-W0, -W1 or -W2). > > What I would like to see are structured warnings. By "structured warnings= " > I mean a system analogous to the Error class, except that a warning would > only emit text to STDERR, not cause the interpreter to exit. In our > hypothetical Warning class you still have backtrace information available= . > And, like Exceptions, there would be a standard hierarchy, with Warning a= t > the top, StandardWarning, UninitializedWarning, RedefinedMethodWarning, > DeprecatedMethodWarning, etc. Whatever we can think of. > > Such a system would allow you to raise specific warnings within your code= : > > ~~~ > class Foo > def old_method > warn DeprecatedMethodWarning, 'This method is deprecated. Use > new_method instead' > # Do stuff > end > end > ~~~ > > The ability to explicitly raise specific types of warnings then makes the= m > testable: > > ~~~ > require 'test/unit' > class TC_Foo_Tests < Test::Unit::TestCase > def setup > @foo =3D Foo.new > end > > # Assume we've added an assert_warn method to Test::Unit > def test_old_method > assert_warn(DeprecatedMethodWarning){ @foo.old_method } > end > end > ~~~ > > And, for sake of backwards compatibility and convenience, a call to > Kernel#warn without an explicit warning type would simply raise a > StandardWarning in the same way that "raise" without an explicit error ty= pe > raises a StandardError. You may be wondering about rescue/retry semantics= . > My opinion on the matter is that warnings should not be rescuable. They a= re > meant to be informational. They are not meant to control program flow. Th= is > also lets us avoid having to worry about retry semantics. Not that anyone > would retry based on a warning in practice. > > Unlike Exceptions you could permanately or temporarily disable warnings t= o > suit your particular preferences in the system I have in mind. For exampl= e, > in the win32-file library I'm well aware that I've gone and redefined som= e > core File methods. When I run any code that uses win32-file with the -w > flag, I get "method redefined" warnings. I don't want to see those becaus= e > I neither need nor want to be reminded about them. So, using our > hypothetical RedefinedMethodWarning class, I could disable them like so: > > ~~~ > RedefinedMethodWarning.disable # No more warnings about method > redefinitions! > ~~~ > > Or, with block syntax, we could disable a particular warning temporarily: > > ~~~ > # Don't bug me about deprecated method warnings within this block, I > know what I'm doing. > DeprecatedMethodWarning.disable{ > [1,2,3,4,5].indexes(1,3) # Array#indexes is a deprecated method > } > ~~~ > > ~~~ > # But here I would get a warning since it's outside the block: > [1,2,3,4,5].indexes(1,3) > ~~~ > > Unlike the current warning system, this would allow users to still receiv= e > other types of warnings, instead of the on/off switch we have now. And, i= n > case you were wondering why the structured_warnings library isn't quite > sufficient, the answer is that it still can=E2=80=99t hook into the exist= ing > warnings being raised in core Ruby via rb_warn(), like uninitialized > variables or redefined methods. > > > > -- > https://bugs.ruby-lang.org/ > --001a113f9708c54cd3052640c6ab Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
#3 is a big deal IMHO. Having finer-grained control over w= hich warnings are shown is long overdue.

=
On Tue, Oct 13, 2015 at 11:25 AM <djberg96@gmail.com> wrote:
Issue #11588 has been reported by Daniel Berger.

----------------------------------------
Feature #11588: Implement structured warnings
https://bugs.ruby-lang.org/issues/11588

* Author: Daniel Berger
* Status: Open
* Priority: Normal
* Assignee:
----------------------------------------
Ruby=E2=80=99s current warning system is lacking. Warnings are controlled b= y the -W flag on the command line, and are generated via the Kernel#warn me= thod within code. There are a host of problems with this approach to warnin= gs.

First, warnings aren=E2=80=99t currently testable. With Test::Unit, for exa= mple, I can ensure that specific errors are raised in certain conditions vi= a the assert_raise method. There is no analogue for warnings. It would be n= ice if there were so I could test them.

Second, there is no backtrace information provided with warnings. If I disc= over a warning I have to wade through the source and figure out where it wa= s generated, because a Kernel#warn call does not provide a line number or m= ethod name that I can refer back to, unless it happened to be generated by = rb_warn(). For large code bases that can be problematic.

Third, and most significantly, with warning flags it=E2=80=99s all or nothi= ng. I cannot enable or disable specific kinds of warnings. Perl, for exampl= e, implements warning control through pragmas. So, for example, I can speci= fy =E2=80=9Cno warnings uninitialized=E2=80=9D in a Perl program and warnin= gs about uninitialized variables go away. With Ruby it=E2=80=99s off, on, o= r even-more-on (-W0, -W1 or -W2).

What I would like to see are structured warnings. By "structured warni= ngs" I mean a system analogous to the Error class, except that a warni= ng would only emit text to STDERR, not cause the interpreter to exit. In ou= r hypothetical Warning class you still have backtrace information available= . And, like Exceptions, there would be a standard hierarchy, with Warning a= t the top, StandardWarning, UninitializedWarning, RedefinedMethodWarning, D= eprecatedMethodWarning, etc. Whatever we can think of.

Such a system would allow you to raise specific warnings within your code:<= br>
~~~
=C2=A0 =C2=A0class Foo
=C2=A0 =C2=A0 =C2=A0 def old_method
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0warn DeprecatedMethodWarning, 'This m= ethod is deprecated. Use new_method instead'
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0# Do stuff
=C2=A0 =C2=A0 =C2=A0 end
=C2=A0 =C2=A0end
~~~

The ability to explicitly raise specific types of warnings then makes them = testable:

~~~
=C2=A0 =C2=A0require 'test/unit'
=C2=A0 =C2=A0class TC_Foo_Tests < Test::Unit::TestCase
=C2=A0 =C2=A0 =C2=A0 def setup
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0@foo =3D Foo.new
=C2=A0 =C2=A0 =C2=A0 end

=C2=A0 =C2=A0 =C2=A0 # Assume we've added an assert_warn method to Test= ::Unit
=C2=A0 =C2=A0 =C2=A0 def test_old_method
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0assert_warn(DeprecatedMethodWarning){ @fo= o.old_method }
=C2=A0 =C2=A0 =C2=A0 end
=C2=A0 =C2=A0end
~~~

And, for sake of backwards compatibility and convenience, a call to Kernel#= warn without an explicit warning type would simply raise a StandardWarning = in the same way that "raise" without an explicit error type raise= s a StandardError. You may be wondering about rescue/retry semantics. My op= inion on the matter is that warnings should not be rescuable. They are mean= t to be informational. They are not meant to control program flow. This als= o lets us avoid having to worry about retry semantics. Not that anyone woul= d retry based on a warning in practice.

Unlike Exceptions you could permanately or temporarily disable warnings to = suit your particular preferences in the system I have in mind. For example,= in the win32-file library I'm well aware that I've gone and redefi= ned some core File methods. When I run any code that uses win32-file with t= he -w flag, I get "method redefined" warnings. I don't want t= o see those because I neither need nor want to be reminded about them. So, = using our hypothetical RedefinedMethodWarning class, I could disable them l= ike so:

~~~
=C2=A0 =C2=A0 RedefinedMethodWarning.disable # No more warnings about metho= d redefinitions!
~~~

Or, with block syntax, we could disable a particular warning temporarily:
~~~
=C2=A0 =C2=A0 # Don't bug me about deprecated method warnings within th= is block, I know what I'm doing.
=C2=A0 =C2=A0 DeprecatedMethodWarning.disable{
=C2=A0 =C2=A0 =C2=A0 =C2=A0[1,2,3,4,5].indexes(1,3) # Array#indexes is a de= precated method
=C2=A0 =C2=A0 }
~~~

~~~
=C2=A0 =C2=A0 # But here I would get a warning since it's outside the b= lock:
=C2=A0 =C2=A0[1,2,3,4,5].indexes(1,3)
~~~

Unlike the current warning system, this would allow users to still receive = other types of warnings, instead of the on/off switch we have now. And, in = case you were wondering why the structured_warnings library isn't quite= sufficient, the answer is that it still can=E2=80=99t hook into the existi= ng warnings being raised in core Ruby via rb_warn(), like uninitialized var= iables or redefined methods.



--
https://bugs.ruby-lang.org/
--001a113f9708c54cd3052640c6ab--