Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

making warnings less scary #59

Closed
wants to merge 10 commits into from

6 participants

@alexch

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
def self._resort! # :nodoc:
@@all.sort! { |a, b|
+ if a.nil? or b.nil?
@evanphx Owner
evanphx added a note

Why is this code here? It should be removed.

@alexch
alexch added a note

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
rescue SignalException, SystemExit
raise
rescue SyntaxError, Exception => e
- warn "Invalid gemspec in [#{file}]: #{e}"
+ warn "Invalid gemspec in [#{file}]: #{e}\n\t#{e.backtrace.first}"
@evanphx Owner
evanphx added a note

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"
# -The RubyGems Team
module Gem
- VERSION = '1.8.1'
+ VERSION = '1.8.2'
@evanphx Owner
evanphx added a note

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

@alexch
alexch added a note

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 @@
# end
module Deprecate
+
+ SKIP_DEFAULT = false
@evanphx Owner
evanphx added a note

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

@alexch
alexch added a note

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.

@evanphx Owner
evanphx added a note

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

@alexch
alexch added a note

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.

@alexch
alexch added a note

...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.

@evanphx Owner
evanphx added a note

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.

@alexch
alexch added a note

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))
yield
ensure
Deprecate.skip = original
end
+ require 'ostruct'
@evanphx Owner
evanphx added a note

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

@alexch
alexch added a note

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

@evanphx Owner
evanphx added a note

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

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")

@WilliamDenniss

+1 to merge this…

@evanphx
Owner

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

@tadman

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

@alexch

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.

@tadman

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.

@zenspider zenspider was assigned
@alexch

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.

@alexch

Related bug ported to github: #84

@practicingruby
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.

@alexch

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!

@practicingruby
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.
View
1  Manifest.txt
@@ -121,6 +121,7 @@ test/rubygems/rubygems_plugin.rb
test/rubygems/sff/discover.rb
test/rubygems/simple_gem.rb
test/rubygems/test_config.rb
+test/rubygems/test_deprecate.rb
test/rubygems/test_gem.rb
test/rubygems/test_gem_builder.rb
test/rubygems/test_gem_command.rb
View
1  lib/rubygems.rb
@@ -112,6 +112,7 @@ class << Gem
# * Daniel Berger -- djberg96(at)gmail.com
# * Phil Hagelberg -- technomancy(at)gmail.com
# * Ryan Davis -- ryand-ruby(at)zenspider.com
+# * Alex Chaffee -- alex(at)stinky.com
#
# (If your name is missing, PLEASE let us know!)
#
View
114 lib/rubygems/deprecate.rb
@@ -21,25 +21,114 @@
# end
module Deprecate
+
+ @skip = nil
def self.skip # :nodoc:
- @skip ||= false
+ @skip.nil? ? false : @skip
end
def self.skip= v # :nodoc:
@skip = v
end
+ def self.saved_warnings # :nodoc:
+ @saved_warnings ||= []
+ end
+
+ def self.add_warning w # :nodoc:
+ warn "Warning: #{w.message} (Further warnings suppressed until exit.)\n#{w.loc}" if saved_warnings.empty?
+ unless saved_warnings.include? w
+ @saved_warnings << w
+ end
+ end
+
+ at_exit do
+ unless Deprecate.saved_warnings.size == 0
+ warn Deprecate.report
+ end
+ end
+
+ def self.report
+ out = "\n"
+ 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"
+ last_warning = nil
+ warnings = @saved_warnings.sort.each do |w|
+ if last_warning and last_warning.target == w.target and last_warning.method_name == w.method_name
+ if last_warning.file == w.file
+ out << ",#{w.line}"
+ else
+ out << "\n#{w.loc}"
+ end
+ else
+ out << "\n#{w.message}\n#{w.loc}"
+ end
+ last_warning = w
+ end
+ out << "\n"
+ out
+ end
+
##
# Temporarily turn off warnings. Intended for tests only.
- def skip_during
- Deprecate.skip, original = true, Deprecate.skip
+ def skip_during(will_skip = true)
+ Deprecate.skip, original = will_skip, Deprecate.skip
yield
ensure
Deprecate.skip = original
end
+ class Warning
+ attr_accessor :target, :method_name, :replacement, :year, :month, :location
+
+ def initialize options
+ @target, @method_name, @replacement, @year, @month, @location =
+ options[:target], options[:method_name], options[:replacement], options[:year], options[:month], options[:location]
+ end
+
+ def ==(other)
+ target == other.target and
+ method_name == other.method_name and
+ location == other.location
+ end
+
+ def <=>(other)
+ self.compare_string <=> other.compare_string
+ end
+
+ def compare_string
+ [target, method_name, file, line].join('|')
+ end
+
+ def to_s
+ [target, method_name, replacement, year, month, location].map(&:inspect).join('|')
+ end
+
+ def message
+ [ "#{target}#{method_name} is deprecated",
+ replacement == :none ? " with no replacement" : "; use #{replacement} instead",
+ ". It will be removed on or after %4d-%02d-01." % [year, month]
+ ].join
+ end
+
+ def loc
+ " called from #{location.join(":")}"
+ end
+
+ def full_name
+ "#{target}#{method_name}"
+ end
+
+ def line
+ location.last
+ end
+
+ def file
+ location.first
+ end
+ end
+
##
# Simple deprecation method that deprecates +name+ by wrapping it up
# in a dummy method. It warns on each call to the dummy method
@@ -51,14 +140,17 @@ def deprecate name, repl, year, month
old = "_deprecated_#{name}"
alias_method old, name
define_method name do |*args, &block| # TODO: really works on 1.8.7?
- klass = self.kind_of? Module
- target = klass ? "#{self}." : "#{self.class}#"
- msg = [ "NOTE: #{target}#{name} is deprecated",
- repl == :none ? " with no replacement" : ", use #{repl}",
- ". It will be removed on or after %4d-%02d-01." % [year, month],
- "\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
- ]
- warn "#{msg.join}." unless Deprecate.skip
+ unless Deprecate.skip
+ warning = Warning.new({
+ :target => (self.kind_of? Module) ? "#{self}." : "#{self.class}#",
+ :method_name => name,
+ :location => Gem.location_of_caller,
+ :replacement => repl,
+ :year => year,
+ :month => month
+ })
+ Deprecate.add_warning warning
+ end
send old, *args, &block
end
}
View
5 lib/rubygems/install_update_options.rb
@@ -115,6 +115,11 @@ def add_install_update_options
"meeting version requirement") do |value, options|
options[:conservative] = true
end
+
+ add_option(:"Install/Update", "--[no-]warnings",
+ "Display warnings about deprecated methods") do |value, options|
+ Deprecate.skip = !value
+ end
end
##
View
202 test/rubygems/test_deprecate.rb
@@ -0,0 +1,202 @@
+require 'rubygems/test_case'
+require 'rubygems/builder'
+require 'rubygems/package'
+
+require 'rubygems/deprecate'
+
+class TestDeprecate < Gem::TestCase
+
+ def setup
+ Deprecate.saved_warnings.clear
+ @original_skip = Deprecate.skip
+ Deprecate.skip = false
+ end
+
+ def teardown
+ Deprecate.saved_warnings.clear
+ Deprecate.skip = @original_skip
+ end
+
+ def test_defaults
+ assert_equal false, @original_skip
+ end
+
+ def test_assignment
+ Deprecate.skip = false
+ assert_equal false, Deprecate.skip
+
+ Deprecate.skip = true
+ assert_equal true, Deprecate.skip
+
+ Deprecate.skip = nil
+ assert([true,false].include? Deprecate.skip)
+ end
+
+ def test_skip
+ Deprecate.skip_during do
+ assert_equal true, Deprecate.skip
+ end
+
+ Deprecate.skip_during(false) do
+ assert_equal false, Deprecate.skip
+ end
+
+ Deprecate.skip_during(nil) do
+ assert_equal false, Deprecate.skip
+ end
+
+ Deprecate.skip = nil
+ end
+
+ ### stolen from Wrong::Helpers
+
+ # Usage:
+ # capturing { puts "hi" } => "hi\n"
+ # capturing(:stderr) { $stderr.puts "hi" } => "hi\n"
+ # out, err = capturing(:stdout, :stderr) { ... }
+ #
+ # see http://www.justskins.com/forums/closing-stderr-105096.html for more explanation
+ def capturing(*streams)
+ streams = [:stdout] if streams.empty?
+ original = {}
+ captured = {}
+
+ # reassign the $ variable (which is used by well-behaved code e.g. puts)
+ streams.each do |stream|
+ original[stream] = (stream == :stdout ? $stdout : $stderr)
+ captured[stream] = StringIO.new
+ reassign_stream(stream, captured)
+ end
+
+ yield
+
+ # return either one string, or an array of two strings
+ if streams.size == 1
+ captured[streams.first].string
+ else
+ [captured[streams[0]].string.to_s, captured[streams[1]].string.to_s]
+ end
+
+ ensure
+
+ streams.each do |stream|
+ # bail if stream was reassigned inside the block
+ if (stream == :stdout ? $stdout : $stderr) != captured[stream]
+ raise "#{stream} was reassigned while being captured"
+ end
+ # support nested calls to capturing
+ original[stream] << captured[stream].string if original[stream].is_a? StringIO
+ reassign_stream(stream, original)
+ end
+ end
+
+ private
+ def reassign_stream(which, streams)
+ case which
+ when :stdout
+ $stdout = streams[which]
+ when :stderr
+ $stderr = streams[which]
+ end
+ end
+ ### end of Wrong::Helpers code
+
+ public
+ def test_has_a_place_to_save_warnings
+ assert_empty Deprecate.saved_warnings
+ end
+
+ class Thing
+ extend Deprecate
+ attr_accessor :message
+ def foo
+ @message = "foo"
+ end
+ def bar
+ @message = "bar"
+ end
+ def goo
+ @message = "goo"
+ end
+ deprecate :foo, :bar, 2099, 3
+ deprecate :goo, :bar, 2099, 4
+ end
+
+ def test_deprecated_method_calls_the_old_method
+ capturing :stderr do
+ thing = Thing.new
+ thing.foo
+ assert_equal "foo", thing.message
+ end
+ end
+
+ def test_deprecated_method_outputs_a_warning
+ out, err = capturing(:stdout, :stderr) do
+ thing = Thing.new
+ thing.foo
+ end
+ assert_equal "", out
+ assert err =~ /Thing#foo is deprecated; use bar instead/, err
+ assert err =~ /on or after 2099-03-01/, err
+ end
+
+ def test_saves_warnings
+ capturing :stderr do
+ thing = Thing.new
+ thing.foo
+ thing.goo
+ assert_equal 2, Deprecate.saved_warnings.size
+ end
+ end
+
+ def test_suppresses_duplicate_warnings
+ capturing :stderr do
+ line1 = line2 = nil
+ thing = Thing.new
+ 3.times do
+ thing.foo; line1 = __LINE__
+ end
+ thing.foo; line2 = __LINE__
+ assert_equal 2, Deprecate.saved_warnings.size
+ assert_equal line1, Deprecate.saved_warnings[0].location.last
+ assert_equal line2, Deprecate.saved_warnings[1].location.last
+ end
+ end
+
+ def test_suppresses_further_warnings_until_exit
+ Deprecate.saved_warnings.clear
+ end
+
+ def test_report
+ err = capturing :stderr do
+ thing = Thing.new
+ line = nil
+ thing.foo; line = __LINE__
+ thing.foo
+ thing.goo
+ thing.foo
+ # also throw one in from a different file
+ Deprecate.add_warning Deprecate::Warning.new({
+ :target => "TestDeprecate::Thing#",
+ :method_name => :foo,
+ :location => ["/some/other/file.rb", 99],
+ :replacement => :bar,
+ :year => 2099,
+ :month => 1
+ })
+ s = Deprecate.report
+ f = __FILE__
+ assert_equal s, <<-REPORT
+
+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.
+
+TestDeprecate::Thing#foo is deprecated; use bar instead. It will be removed on or after 2099-03-01.
+ called from #{f}:#{line},#{line+1},#{line+3}
+ called from /some/other/file.rb:99
+TestDeprecate::Thing#goo is deprecated; use bar instead. It will be removed on or after 2099-04-01.
+ called from #{f}:#{line+2}
+ REPORT
+
+ end
+ end
+end
Something went wrong with that request. Please try again.