Skip to content
This repository

making warnings less scary #59

Closed
wants to merge 10 commits into from

6 participants

Alex Chaffee William Denniss Evan Phoenix Scott Tadman Gregory Brown Ryan Davis
Alex Chaffee
alexch commented May 11, 2011

https://rubyforge.org/tracker/index.php?func=detail&aid=29176&group_id=126&atid=575

rubygems still prints out deprecation warnings, but after the first warning, it buffers up all remaining warnings and then emits them at_exit in a somewhat nicer report format. (See the last test in test_deprecate.rb for a sample.)

I also pulled in the "--no-warnings" patch, but set the default back to "show all warnings" (i.e. skip=false).

lib/rubygems/specification.rb
@@ -274,6 +274,9 @@ class Gem::Specification
274 274
 
275 275
   def self._resort! # :nodoc:
276 276
     @@all.sort! { |a, b|
  277
+      if a.nil? or b.nil?
2
Evan Phoenix Owner
evanphx added a note May 11, 2011

Why is this code here? It should be removed.

Alex Chaffee
alexch added a note May 11, 2011

oops

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/rubygems/specification.rb
@@ -547,7 +550,7 @@ class Gem::Specification
547 550
     rescue SignalException, SystemExit
548 551
       raise
549 552
     rescue SyntaxError, Exception => e
550  
-      warn "Invalid gemspec in [#{file}]: #{e}"
  553
+      warn "Invalid gemspec in [#{file}]: #{e}\n\t#{e.backtrace.first}"
1
Evan Phoenix Owner
evanphx added a note May 11, 2011

Remove the backtrace stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/rubygems.rb
@@ -120,7 +121,7 @@ require "rubygems/deprecate"
120 121
 # -The RubyGems Team
121 122
 
122 123
 module Gem
123  
-  VERSION = '1.8.1'
  124
+  VERSION = '1.8.2'
2
Evan Phoenix Owner
evanphx added a note May 11, 2011

Remove this. Only the release manager gets to change the version.

Alex Chaffee
alexch added a note May 11, 2011

oops

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/rubygems/deprecate.rb
@@ -21,25 +21,76 @@
21 21
 #     end
22 22
 
23 23
 module Deprecate
  24
+  
  25
+  SKIP_DEFAULT = false
7
Evan Phoenix Owner
evanphx added a note May 11, 2011

What is the point of this constant? I only see it used to have it set to false.

Alex Chaffee
alexch added a note May 11, 2011

So if someone wants to rebuild the gem with the default set to true, it'll be dead easy for them. This was a compromise with tadman's patch
https://gist.github.com/965921 which set skip to true by default, but the logic was weird so I extracted it and put in a test so it works right no matter what the default value is. Since the warnings can occur any time the rubygems library is used, we needed some global mechanism to set the user's preference. Rebuilding the gem is clearly inferior to, e.g., a config file, but much easier to code.

Evan Phoenix Owner
evanphx added a note May 11, 2011

What is "the gem" in this case? I can't imagine you mean that an individual gem would use const_set to change this.

Alex Chaffee
alexch added a note May 11, 2011

The outcry to the deprecation spam (which I prefer to call "warningasm") is so great that some people (e.g. tadman) are resorting to building and installing their own version of RubyGems, so "the gem" is rubygems-update, or what you get from "rake gem" in the rubygems source dir.

Alex Chaffee
alexch added a note May 11, 2011

...note that this dubious feature is a minor aspect of this patch, and I'd be happy to separate it. I pulled in tadman's patch since he'd already written a test for Deprecate so I could get started coding right away.

Evan Phoenix Owner
evanphx added a note May 11, 2011

If a person goes all that distance, I think they should have to figure out how skip works and change the false to a true in the method.

Alex Chaffee
alexch added a note May 18, 2011

OK, code has been re-obfuscated :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/rubygems/deprecate.rb
((54 lines not shown))
38 71
     yield
39 72
   ensure
40 73
     Deprecate.skip = original
41 74
   end
42 75
 
  76
+  require 'ostruct'
3
Evan Phoenix Owner
evanphx added a note May 11, 2011

Adding other dep to rubygems is a no-no. This should go and Warning should just be a normal object.

Alex Chaffee
alexch added a note May 11, 2011

Roger that. Though I thought ostruct was a standard library... is it not in all distros?

Evan Phoenix Owner
evanphx added a note May 11, 2011

It is, but that doesn't matter. There is no reason for ostruct to be used here when a normal object will do. It just adds another unnecessary dep to rubygems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Alex Chaffee
alexch commented May 12, 2011

Made Evan's fixes. Also made the report more concise (it puts all lines from the same file together, e.g. "foo.rb:12,15,345")

William Denniss

+1 to merge this…

Evan Phoenix
Owner

alexch, thanks for the fixes. We're still discussing this a little bit, hopefully should get it figured out today.

Scott Tadman
tadman commented June 02, 2011

This also includes a unit test for Deprecate, now Gem::Deprecate, which is worryingly not present in the master branch.

Alex Chaffee
alexch commented June 07, 2011

It looks like a bunch of the warning spam has been removed as of RubyGems 1.8.5, especially thanks to
21cccd5
and
fe1367c

I still think this patch is worthwhile, mostly because it has unit tests for the Deprecate module (as tadman pointed out and first added). If you'd like me to rebase to master and bring it up to snuff, I'd be happy to, but if you're going to pass on it, that's cool too; please let me know your decision either way.

Scott Tadman
tadman commented June 07, 2011

I have another patch which is desperately required for 1.8.5: #70

I am getting slammed with add_specs deprecation warnings because of Bundler 1.0.14. This turns a sea of warnings into a quiet little grumble.

If it's practical, that should be merged in with this by whomever is undertaking the next build. I would have supplied a patch for the unit test, too, but that is still not in the main branch and I can't patch against nothing.

Alex Chaffee
alexch commented June 08, 2011

It looks like the majority of the warning noise has been suppressed as of Rubygems 1.8.5, which is great! However, the possibility of multiple (redundant, noisy, easily ignored) warnings popping up later (cf. tadman's pull 70) means there's still an underlying flaw in the way rubygems reports warnings. This patch would close that door.

Alex Chaffee
alexch commented June 08, 2011

Related bug ported to github: #84

Gregory Brown sandal closed this August 14, 2011
Gregory Brown sandal reopened this August 14, 2011
Gregory Brown
Collaborator

Hi @alexch,

Finally got some answers about this. It doesn't look like this patch is going to get merged, because the RubyGems maintainers have been burned in the past by removing deprecated APIs that they didn't warn people in a sufficiently noisy way about. I know this is a controversial decision, but with the case-by-case reduction in the amount of warnings being outputted, this is increasingly becoming a corner case issue for those working with legacy applications.

As for the tests you added to Deprecate, those won't be completely lost in the shuffle. This will be worked on by @zenspider, and I've created ticket #167 to track this issue.

I am now closing this ticket, but thanks for working on this. Even if the team is going another direction, I appreciate that you were trying to help out with this problem.

Gregory Brown sandal closed this August 25, 2011
Alex Chaffee

Sigh. I wish they would see that they're way past "sufficiently" and into "counterproductively" noisy, but whatever.

Thanks for being so responsive and great, @sandal!

Gregory Brown
Collaborator

@alexch: I'll address the point about noisiness over on #84, I'm about to close that as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
1  Manifest.txt
@@ -121,6 +121,7 @@ test/rubygems/rubygems_plugin.rb
121 121
 test/rubygems/sff/discover.rb
122 122
 test/rubygems/simple_gem.rb
123 123
 test/rubygems/test_config.rb
  124
+test/rubygems/test_deprecate.rb
124 125
 test/rubygems/test_gem.rb
125 126
 test/rubygems/test_gem_builder.rb
126 127
 test/rubygems/test_gem_command.rb
1  lib/rubygems.rb
@@ -112,6 +112,7 @@ class << Gem
112 112
 # * Daniel Berger      -- djberg96(at)gmail.com
113 113
 # * Phil Hagelberg     -- technomancy(at)gmail.com
114 114
 # * Ryan Davis         -- ryand-ruby(at)zenspider.com
  115
+# * Alex Chaffee       -- alex(at)stinky.com
115 116
 #
116 117
 # (If your name is missing, PLEASE let us know!)
117 118
 #
114  lib/rubygems/deprecate.rb
@@ -21,25 +21,114 @@
21 21
 #     end
22 22
 
23 23
 module Deprecate
  24
+  
  25
+  @skip = nil
24 26
 
25 27
   def self.skip # :nodoc:
26  
-    @skip ||= false
  28
+    @skip.nil? ? false : @skip
27 29
   end
28 30
 
29 31
   def self.skip= v # :nodoc:
30 32
     @skip = v
31 33
   end
32 34
 
  35
+  def self.saved_warnings # :nodoc:
  36
+    @saved_warnings ||= []
  37
+  end
  38
+  
  39
+  def self.add_warning w  # :nodoc:
  40
+    warn "Warning: #{w.message} (Further warnings suppressed until exit.)\n#{w.loc}" if saved_warnings.empty?
  41
+    unless saved_warnings.include? w
  42
+      @saved_warnings << w
  43
+    end
  44
+  end
  45
+
  46
+  at_exit do
  47
+    unless Deprecate.saved_warnings.size == 0
  48
+      warn Deprecate.report
  49
+    end
  50
+  end
  51
+  
  52
+  def self.report
  53
+    out = "\n"
  54
+    out << "Some of your installed gems called deprecated methods. See http://blog.zenspider.com/2011/05/rubygems-18-is-coming.html for background. Use 'gem pristine --all' to fix or 'rubygems update --system 1.7.2' to downgrade.\n"
  55
+    last_warning = nil
  56
+    warnings = @saved_warnings.sort.each do |w|
  57
+      if last_warning and last_warning.target == w.target and last_warning.method_name == w.method_name
  58
+        if last_warning.file == w.file
  59
+          out << ",#{w.line}"
  60
+        else
  61
+          out << "\n#{w.loc}"
  62
+        end
  63
+      else
  64
+        out << "\n#{w.message}\n#{w.loc}"
  65
+      end
  66
+      last_warning = w
  67
+    end
  68
+    out << "\n"
  69
+    out
  70
+  end
  71
+
33 72
   ##
34 73
   # Temporarily turn off warnings. Intended for tests only.
35 74
 
36  
-  def skip_during
37  
-    Deprecate.skip, original = true, Deprecate.skip
  75
+  def skip_during(will_skip = true)
  76
+    Deprecate.skip, original = will_skip, Deprecate.skip
38 77
     yield
39 78
   ensure
40 79
     Deprecate.skip = original
41 80
   end
42 81
 
  82
+  class Warning
  83
+    attr_accessor :target, :method_name, :replacement, :year, :month, :location
  84
+
  85
+    def initialize options
  86
+      @target, @method_name, @replacement, @year, @month, @location =
  87
+      options[:target], options[:method_name], options[:replacement], options[:year], options[:month], options[:location]
  88
+    end
  89
+    
  90
+    def ==(other)
  91
+      target == other.target and
  92
+      method_name == other.method_name and
  93
+      location == other.location
  94
+    end
  95
+    
  96
+    def <=>(other)
  97
+      self.compare_string <=> other.compare_string
  98
+    end
  99
+    
  100
+    def compare_string
  101
+      [target, method_name, file, line].join('|')
  102
+    end
  103
+    
  104
+    def to_s
  105
+      [target, method_name, replacement, year, month, location].map(&:inspect).join('|')
  106
+    end
  107
+    
  108
+    def message
  109
+      [ "#{target}#{method_name} is deprecated",
  110
+              replacement == :none ? " with no replacement" : "; use #{replacement} instead",
  111
+              ". It will be removed on or after %4d-%02d-01." % [year, month]
  112
+      ].join
  113
+    end
  114
+    
  115
+    def loc
  116
+      "  called from #{location.join(":")}"
  117
+    end
  118
+    
  119
+    def full_name
  120
+      "#{target}#{method_name}"
  121
+    end
  122
+    
  123
+    def line
  124
+      location.last
  125
+    end
  126
+
  127
+    def file
  128
+      location.first
  129
+    end
  130
+  end
  131
+
43 132
   ##
44 133
   # Simple deprecation method that deprecates +name+ by wrapping it up
45 134
   # in a dummy method. It warns on each call to the dummy method
@@ -51,14 +140,17 @@ def deprecate name, repl, year, month
51 140
       old = "_deprecated_#{name}"
52 141
       alias_method old, name
53 142
       define_method name do |*args, &block| # TODO: really works on 1.8.7?
54  
-        klass = self.kind_of? Module
55  
-        target = klass ? "#{self}." : "#{self.class}#"
56  
-        msg = [ "NOTE: #{target}#{name} is deprecated",
57  
-                repl == :none ? " with no replacement" : ", use #{repl}",
58  
-                ". It will be removed on or after %4d-%02d-01." % [year, month],
59  
-                "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
60  
-              ]
61  
-        warn "#{msg.join}." unless Deprecate.skip
  143
+        unless Deprecate.skip
  144
+          warning = Warning.new({
  145
+            :target => (self.kind_of? Module) ? "#{self}." : "#{self.class}#",
  146
+            :method_name => name,
  147
+            :location => Gem.location_of_caller,
  148
+            :replacement => repl,
  149
+            :year => year,
  150
+            :month => month
  151
+          })
  152
+          Deprecate.add_warning warning
  153
+        end
62 154
         send old, *args, &block
63 155
       end
64 156
     }
5  lib/rubygems/install_update_options.rb
@@ -115,6 +115,11 @@ def add_install_update_options
115 115
                 "meeting version requirement") do |value, options|
116 116
       options[:conservative] = true
117 117
     end
  118
+    
  119
+    add_option(:"Install/Update", "--[no-]warnings",
  120
+                "Display warnings about deprecated methods") do |value, options|
  121
+      Deprecate.skip = !value
  122
+    end
118 123
   end
119 124
 
120 125
   ##
202  test/rubygems/test_deprecate.rb
... ...
@@ -0,0 +1,202 @@
  1
+require 'rubygems/test_case'
  2
+require 'rubygems/builder'
  3
+require 'rubygems/package'
  4
+
  5
+require 'rubygems/deprecate'
  6
+
  7
+class TestDeprecate < Gem::TestCase
  8
+
  9
+  def setup
  10
+    Deprecate.saved_warnings.clear
  11
+    @original_skip = Deprecate.skip
  12
+    Deprecate.skip = false
  13
+  end
  14
+  
  15
+  def teardown
  16
+    Deprecate.saved_warnings.clear
  17
+    Deprecate.skip = @original_skip
  18
+  end
  19
+
  20
+  def test_defaults
  21
+    assert_equal false, @original_skip
  22
+  end
  23
+
  24
+  def test_assignment
  25
+    Deprecate.skip = false
  26
+    assert_equal false, Deprecate.skip
  27
+
  28
+    Deprecate.skip = true
  29
+    assert_equal true, Deprecate.skip
  30
+
  31
+    Deprecate.skip = nil
  32
+    assert([true,false].include? Deprecate.skip)
  33
+  end
  34
+
  35
+  def test_skip
  36
+    Deprecate.skip_during do
  37
+      assert_equal true, Deprecate.skip
  38
+    end
  39
+
  40
+    Deprecate.skip_during(false) do
  41
+      assert_equal false, Deprecate.skip
  42
+    end
  43
+
  44
+    Deprecate.skip_during(nil) do
  45
+      assert_equal false, Deprecate.skip
  46
+    end
  47
+
  48
+    Deprecate.skip = nil
  49
+  end
  50
+
  51
+  ### stolen from Wrong::Helpers
  52
+
  53
+  # Usage:
  54
+  # capturing { puts "hi" } => "hi\n"
  55
+  # capturing(:stderr) { $stderr.puts "hi" } => "hi\n"
  56
+  # out, err = capturing(:stdout, :stderr) { ... }
  57
+  #
  58
+  # see http://www.justskins.com/forums/closing-stderr-105096.html for more explanation
  59
+  def capturing(*streams)
  60
+    streams = [:stdout] if streams.empty?
  61
+    original = {}
  62
+    captured = {}
  63
+
  64
+    # reassign the $ variable (which is used by well-behaved code e.g. puts)
  65
+    streams.each do |stream|
  66
+      original[stream] = (stream == :stdout ? $stdout : $stderr)
  67
+      captured[stream] = StringIO.new
  68
+      reassign_stream(stream, captured)
  69
+    end
  70
+
  71
+    yield
  72
+
  73
+    # return either one string, or an array of two strings
  74
+    if streams.size == 1
  75
+      captured[streams.first].string
  76
+    else
  77
+      [captured[streams[0]].string.to_s, captured[streams[1]].string.to_s]
  78
+    end
  79
+
  80
+  ensure
  81
+
  82
+    streams.each do |stream|
  83
+      # bail if stream was reassigned inside the block
  84
+      if (stream == :stdout ? $stdout : $stderr) != captured[stream]
  85
+        raise "#{stream} was reassigned while being captured"
  86
+      end
  87
+      # support nested calls to capturing
  88
+      original[stream] << captured[stream].string if original[stream].is_a? StringIO
  89
+      reassign_stream(stream, original)
  90
+    end
  91
+  end
  92
+
  93
+  private
  94
+  def reassign_stream(which, streams)
  95
+    case which
  96
+    when :stdout
  97
+      $stdout = streams[which]
  98
+    when :stderr
  99
+      $stderr = streams[which]
  100
+    end
  101
+  end
  102
+  ### end of Wrong::Helpers code
  103
+
  104
+  public
  105
+  def test_has_a_place_to_save_warnings
  106
+    assert_empty Deprecate.saved_warnings    
  107
+  end
  108
+
  109
+  class Thing
  110
+    extend Deprecate
  111
+    attr_accessor :message
  112
+    def foo
  113
+      @message = "foo"
  114
+    end
  115
+    def bar
  116
+      @message = "bar"
  117
+    end
  118
+    def goo
  119
+      @message = "goo"
  120
+    end
  121
+    deprecate :foo, :bar, 2099, 3
  122
+    deprecate :goo, :bar, 2099, 4
  123
+  end
  124
+
  125
+  def test_deprecated_method_calls_the_old_method
  126
+    capturing :stderr do
  127
+      thing = Thing.new
  128
+      thing.foo
  129
+      assert_equal "foo", thing.message
  130
+    end
  131
+  end
  132
+
  133
+  def test_deprecated_method_outputs_a_warning
  134
+    out, err = capturing(:stdout, :stderr) do
  135
+      thing = Thing.new
  136
+      thing.foo
  137
+    end
  138
+    assert_equal "", out
  139
+    assert err =~ /Thing#foo is deprecated; use bar instead/, err
  140
+    assert err =~ /on or after 2099-03-01/, err
  141
+  end
  142
+
  143
+  def test_saves_warnings
  144
+    capturing :stderr do
  145
+      thing = Thing.new
  146
+      thing.foo
  147
+      thing.goo
  148
+      assert_equal 2, Deprecate.saved_warnings.size
  149
+    end
  150
+  end
  151
+
  152
+  def test_suppresses_duplicate_warnings
  153
+    capturing :stderr do
  154
+      line1 = line2 = nil
  155
+      thing = Thing.new
  156
+      3.times do
  157
+        thing.foo; line1 = __LINE__
  158
+      end
  159
+      thing.foo; line2 = __LINE__
  160
+      assert_equal 2, Deprecate.saved_warnings.size
  161
+      assert_equal line1, Deprecate.saved_warnings[0].location.last
  162
+      assert_equal line2, Deprecate.saved_warnings[1].location.last
  163
+    end
  164
+  end
  165
+
  166
+  def test_suppresses_further_warnings_until_exit
  167
+    Deprecate.saved_warnings.clear
  168
+  end
  169
+
  170
+  def test_report
  171
+    err = capturing :stderr do      
  172
+      thing = Thing.new
  173
+      line = nil
  174
+      thing.foo; line = __LINE__
  175
+      thing.foo
  176
+      thing.goo
  177
+      thing.foo
  178
+      # also throw one in from a different file
  179
+      Deprecate.add_warning Deprecate::Warning.new({
  180
+        :target => "TestDeprecate::Thing#",
  181
+        :method_name => :foo,
  182
+        :location => ["/some/other/file.rb", 99],
  183
+        :replacement => :bar,
  184
+        :year => 2099,
  185
+        :month => 1
  186
+      })
  187
+      s = Deprecate.report
  188
+      f = __FILE__
  189
+      assert_equal s, <<-REPORT
  190
+
  191
+Some of your installed gems called deprecated methods. See http://blog.zenspider.com/2011/05/rubygems-18-is-coming.html for background. Use 'gem pristine --all' to fix or 'rubygems update --system 1.7.2' to downgrade.
  192
+
  193
+TestDeprecate::Thing#foo is deprecated; use bar instead. It will be removed on or after 2099-03-01.
  194
+  called from #{f}:#{line},#{line+1},#{line+3}
  195
+  called from /some/other/file.rb:99
  196
+TestDeprecate::Thing#goo is deprecated; use bar instead. It will be removed on or after 2099-04-01.
  197
+  called from #{f}:#{line+2}
  198
+      REPORT
  199
+      
  200
+    end
  201
+  end
  202
+end
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.