Please review pull request #592: Bug/2.7.x/incorrect dynamic scope warnings opened by (hunner)
Description:
Until the scope inheritance is completely fixed for the new non-dynamic scoping model, we should give appropriate dynamic scoping deprecation warnings.
This branch will perform two queries; one with the dynamic scoping and a second one that directly references the local scope or top/node scope. If the answers differ then removing dynamic scoping would affect the puppet run and a warning should be given.
- Opened: Fri Mar 23 22:07:58 UTC 2012
- Based on: puppetlabs:master (0b900c9548f6c652f0d7271156b69c35aa232ead)
- Requested merge: hunner:bug/2.7.x/incorrect_dynamic_scope-warnings (fa2be2691dd1ba65eb871690d16b540c32ec1453)
Diff follows:
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 746cfbd..24b725e 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -21,7 +21,7 @@ class Puppet::Parser::Scope
attr_accessor :source, :resource
attr_accessor :base, :keyword
attr_accessor :top, :translated, :compiler
- attr_accessor :parent, :dynamic
+ attr_accessor :parent
attr_reader :namespaces
# thin wrapper around an ephemeral
@@ -222,13 +222,48 @@ def qualified_scope(classname)
private :qualified_scope
- # Look up a variable. The simplest value search we do.
+ # Look up a variable with traditional scoping and then with new scoping. If
+ # the answers differ then print a deprecation warning.
def lookupvar(name, options = {})
+ oldval = oldlookupvar(name,options)
+ newval = newlookupvar(name,options)
+ if oldval and newval and newval != oldval
+ location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
+ Puppet.deprecation_warning "Dynamic lookup of $#{name}#{location} is deprecated. Support will be removed in a later version of Puppet. Use a fully-qualified variable name (e.g., $classname::variable) or parameterized classes."
+ end
+ oldval
+ end
+
+ # Look up a variable. The simplest value search we do.
+ def newlookupvar(name, options = {})
+ # Save the originating scope for the request
+ options[:origin] = self unless options[:origin]
+ table = ephemeral?(name) ? @ephemeral.last : @symtable
+ if name =~ /^(.*)::(.+)$/
+ begin
+ qualified_scope($1).newlookupvar($2,options.merge({:origin => nil}))
+ rescue RuntimeError => e
+ location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
+ warning "Could not look up qualified variable '#{name}'; #{e.message}#{location}"
+ :undefined
+ end
+ # If the value is present and either we are top/node scope or originating scope...
+ elsif (ephemeral_include?(name) or table.include?(name)) and (compiler and self == compiler.topscope or (self.resource and self.resource.type == "Node") or self == options[:origin])
+ table[name]
+ elsif parent
+ parent.newlookupvar(name,options)
+ else
+ :undefined
+ end
+ end
+
+ # Look up a variable. The simplest value search we do.
+ def oldlookupvar(name, options = {})
table = ephemeral?(name) ? @ephemeral.last : @symtable
# If the variable is qualified, then find the specified scope and look the variable up there instead.
if name =~ /^(.*)::(.+)$/
begin
- qualified_scope($1).lookupvar($2,options)
+ qualified_scope($1).oldlookupvar($2,options)
rescue RuntimeError => e
location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
warning "Could not look up qualified variable '#{name}'; #{e.message}#{location}"
@@ -236,13 +271,9 @@ def lookupvar(name, options = {})
end
elsif ephemeral_include?(name) or table.include?(name)
# We can't use "if table[name]" here because the value might be false
- if options[:dynamic] and self != compiler.topscope
- location = (options[:file] && options[:line]) ? " at #{options[:file]}:#{options[:line]}" : ''
- Puppet.deprecation_warning "Dynamic lookup of $#{name}#{location} is deprecated. Support will be removed in Puppet 2.8. Use a fully-qualified variable name (e.g., $classname::variable) or parameterized classes."
- end
table[name]
elsif parent
- parent.lookupvar(name,options.merge(:dynamic => (dynamic || options[:dynamic])))
+ parent.oldlookupvar(name,options)
else
:undefined
end
@@ -374,7 +405,7 @@ def unset_ephemeral_var(level=:all)
# check if name exists in one of the ephemeral scope.
def ephemeral_include?(name)
- @ephemeral.reverse.each do |eph|
+ @ephemeral.reverse_each do |eph|
return true if eph.include?(name)
end
false
diff --git a/lib/puppet/resource/type.rb b/lib/puppet/resource/type.rb
index 600f449..16ed977 100644
--- a/lib/puppet/resource/type.rb
+++ b/lib/puppet/resource/type.rb
@@ -66,7 +66,7 @@ def evaluate_code(resource)
static_parent = evaluate_parent_type(resource)
scope = static_parent || resource.scope
- scope = scope.newscope(:namespace => namespace, :source => self, :resource => resource, :dynamic => !static_parent) unless resource.title == :main
+ scope = scope.newscope(:namespace => namespace, :source => self, :resource => resource) unless resource.title == :main
scope.compiler.add_class(name) unless definition?
set_resource_parameters(resource, scope)
diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb
index 67f0db0..b36ca6d 100755
--- a/spec/unit/parser/scope_spec.rb
+++ b/spec/unit/parser/scope_spec.rb
@@ -3,11 +3,11 @@
describe Puppet::Parser::Scope do
before :each do
- @topscope = Puppet::Parser::Scope.new
# This is necessary so we don't try to use the compiler to discover our parent.
- @topscope.parent = nil
@scope = Puppet::Parser::Scope.new
@scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo"))
+ @scope.source = Puppet::Resource::Type.new(:node, :foo)
+ @topscope = @scope.compiler.topscope
@scope.parent = @topscope
end
@@ -92,14 +92,6 @@
Puppet::Parser::Scope.new.singleton_class.ancestors.should be_include(mod)
end
-
- it "should remember if it is dynamic" do
- (!!Puppet::Parser::Scope.new(:dynamic => true).dynamic).should == true
- end
-
- it "should assume it is not dynamic" do
- (!Puppet::Parser::Scope.new.dynamic).should == true
- end
end
describe "when looking up a variable" do
@@ -128,10 +120,20 @@
@scope.lookupvar("var").should == "childval"
end
+ it "should be able to look up intermediary variables in parent scopes (DEPRECATED)" do
+ Puppet.expects(:deprecation_warning)
+ thirdscope = Puppet::Parser::Scope.new
+ thirdscope.parent = @scope
+ thirdscope.source = Puppet::Resource::Type.new(:hostclass, :foo, :module_name => "foo")
+ @scope.source = Puppet::Resource::Type.new(:hostclass, :bar, :module_name => "bar")
+
+ @topscope.setvar("var2","parentval")
+ @scope.setvar("var2","childval")
+ thirdscope.lookupvar("var2").should == "childval"
+ end
+
describe "and the variable is qualified" do
- before do
- @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode"))
- @scope.compiler = @compiler
+ before :each do
@known_resource_types = @scope.known_resource_types
end
@@ -151,6 +153,7 @@ def create_class_scope(name)
end
it "should be able to look up explicitly fully qualified variables from main" do
+ Puppet.expects(:deprecation_warning).never
other_scope = create_class_scope("")
other_scope.setvar("othervar", "otherval")
@@ -159,6 +162,7 @@ def create_class_scope(name)
end
it "should be able to look up explicitly fully qualified variables from other scopes" do
+ Puppet.expects(:deprecation_warning).never
other_scope = create_class_scope("other")
other_scope.setvar("var", "otherval")
@@ -167,6 +171,7 @@ def create_class_scope(name)
end
it "should be able to look up deeply qualified variables" do
+ Puppet.expects(:deprecation_warning).never
other_scope = create_class_scope("other::deep::klass")
other_scope.setvar("var", "otherval")
@@ -175,28 +180,33 @@ def create_class_scope(name)
end
it "should return ':undefined' for qualified variables that cannot be found in other classes" do
+ Puppet.expects(:deprecation_warning).never
other_scope = create_class_scope("other::deep::klass")
@scope.lookupvar("other::deep::klass::var").should == :undefined
end
it "should warn and return ':undefined' for qualified variables whose classes have not been evaluated" do
+ Puppet.expects(:deprecation_warning).never
klass = newclass("other::deep::klass")
- @scope.expects(:warning)
+ @scope.expects(:warning).at_least_once
@scope.lookupvar("other::deep::klass::var").should == :undefined
end
it "should warn and return ':undefined' for qualified variables whose classes do not exist" do
- @scope.expects(:warning)
+ Puppet.expects(:deprecation_warning).never
+ @scope.expects(:warning).at_least_once
@scope.lookupvar("other::deep::klass::var").should == :undefined
end
it "should return ':undefined' when asked for a non-string qualified variable from a class that does not exist" do
+ Puppet.expects(:deprecation_warning).never
@scope.stubs(:warning)
@scope.lookupvar("other::deep::klass::var").should == :undefined
end
it "should return ':undefined' when asked for a non-string qualified variable from a class that has not been evaluated" do
+ Puppet.expects(:deprecation_warning).never
@scope.stubs(:warning)
klass = newclass("other::deep::klass")
@scope.lookupvar("other::deep::klass::var").should == :undefined
diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb
index 352f767..19f86e7 100755
--- a/spec/unit/resource/type_spec.rb
+++ b/spec/unit/resource/type_spec.rb
@@ -238,7 +238,7 @@ def double_convert
describe "when setting its parameters in the scope" do
before do
- @scope = Puppet::Parser::Scope.new(:compiler => stub("compiler", :environment => Puppet::Node::Environment.new), :source => stub("source"))
+ @scope = Puppet::Parser::Scope.new(:compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("foo")), :source => stub("source"))
@resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope)
@type = Puppet::Resource::Type.new(:hostclass, "foo")
end
@@ -435,7 +435,7 @@ def double_convert
it "should set all of its parameters in a subscope" do
subscope = stub 'subscope', :compiler => @compiler
- @scope.expects(:newscope).with(:source => @type, :dynamic => true, :namespace => 'foo', :resource => @resource).returns subscope
+ @scope.expects(:newscope).with(:source => @type, :namespace => 'foo', :resource => @resource).returns subscope
@type.expects(:set_resource_parameters).with(@resource, subscope)
@type.evaluate_code(@resource)
-- You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
