Bug #13210

undef changes into '' when passed to function as a param

Added by Timur Batyrshin over 1 year ago. Updated 23 days ago.

Status:DuplicateStart date:03/19/2012
Priority:NormalDue date:
Assignee:-% Done:

0%

Category:-
Target version:-
Affected Puppet version: Branch:
Keywords:

Description

Suppose we have a manifest like that:

define foo ( $x = '1' ) {
  notify { "x${x}": }
}

class bar {
  $x = myfunc( undef )
  foo {'no1': x=> $x }
}

class baz {
  $x = undef
  foo {'no2': x=> $x }
}


include bar
include baz

With myfunc() defined like that:

module Puppet::Parser::Functions
    newfunction(:myfunc, :type => :rvalue) do |param|
      return param
    end
end

When applying the manifest we get the following output.

notice: x
notice: /Stage[main]/Bar/Foo[no1]/Notify[x]/message: defined 'message' as 'x'
notice: x1
notice: /Stage[main]/Baz/Foo[no2]/Notify[x1]/message: defined 'message' as 'x1'
notice: Finished catalog run in 0.06 seconds

However the same message should be produced in both calls to foo.


Related issues

Related to Puppet - Bug #4692: undefined variables cause :undef to be passed to functions Closed 09/02/2010
Duplicated by Puppet - Bug #15329: Puppet lacks a proper "undefined" value Accepted 07/02/2012

History

#1 Updated by Kelsey Hightower about 1 year ago

Timur,

Thanks for reporting this issue, which version of Puppet are you running?

#2 Updated by Timur Batyrshin about 1 year ago

2.7.11 I believe I’ve seen that on 2.7.10 too

#3 Updated by Chris Price about 1 year ago

  • Status changed from Unreviewed to Investigating
  • Assignee set to Chris Price

As documented in the linked ticket (#4692), this behavior is intentional.

I will discuss this with a few other folks around the office—it seems to me that the expected behavior described by this ticket might make more sense, because users could theoretically write their functions to handle :undef; whereas with the current behavior there is no way to detect / handle the case that this ticket describes.

However, the linked ticket seems to indicate that there was significant momentum in the community in favor of the current behavior.

#4 Updated by Timur Batyrshin about 1 year ago

The way I needed the behaviour of passing :undef (or nil) into function is stacking classes/defines with hiera like:

class foo ( $x = hiera('value_x', undef)) {

# some code here

  class { 'foo:bar':
    x => $x
  }
}

class foo:bar ( $x = undef ) {

# do some processing with $x

}

Thus if you don’t have value_x defined in hiera you pass the undef to wrapped class.

Do you think converting undef into nil rather than into ‘’ will break much things? In ruby nil is often essentially the same as undef in puppet whereas ‘’ is not.

#5 Updated by Chris Price about 1 year ago

My understanding is that there may be a fair amount of existing code out there in the world that does things like call “split” on a function parameter, without checking first to verify that the parameter is a string. Assuming that’s the issue of concern expressed in the previous ticket, then it won’t matter whether we pass a symbol or nil; either one will break those functions.

However, from what I can see so far, I think that you are correct that the current behavior eliminates the ability to handle certain use cases like yours.

Will try to dig up a few more historical details and then update this ticket.

#6 Updated by Chris Price about 1 year ago

I discussed this with Matt Robinson, who had been involved to some degree with the original ticket. His recollection was that there were arguments for both of the two approaches—so it does not sound like there was an overwhelming argument in support of the current behavior.

It seems to me that an important consideration here is: “what is the target audience of the function definition API?” If we’re targeting developers, then I don’t think it’s unreasonable to expect authors to know that they may need to account for nil or :undef values being passed in as arguments. If we’re targeting a broader audience and making the function definition as simple as possible is the primary goal, then I can perhaps see the case for passing undefs as empty strings.

My overall take on this, though, is that there are cases in the world where there is a meaningful semantic distinction between “null” and an empty string (plenty of precedent for this in relational databases, etc.), and that our current implementation explicitly prevents users from handling that distinction. Thus I’d be inclined to mark this ticket as accepted and propose that we change the behavior… probably to passing ‘nil’.

Will leave this ticket open for comments for a while and see if there are strong feelings one way or the other.

#7 Updated by Chris Price about 1 year ago

  • Status changed from Investigating to Needs Decision

#8 Updated by R.I. Pienaar about 1 year ago

I’m in favour of making undef behave like ruby nil in templates and functions, might be a painful transition but ultimately it means any beginners guide to Ruby actually applies to our code where right now almost every sysadmin who comes to Ruby via puppet finds it a rather painful experience.

#9 Updated by Daniel Pittman about 1 year ago

R.I. Pienaar wrote:

I’m in favour of making undef behave like ruby nil in templates and functions, might be a painful transition but ultimately it means any beginners guide to Ruby actually applies to our code where right now almost every sysadmin who comes to Ruby via puppet finds it a rather painful experience.

This is the right answer: we shouldn’t use :undef anywhere except for our own convenience internally. It shouldn’t be visible to anyone outside of core – not custom functions, custom types, providers, or anything. We should use Ruby nil everywhere that we currently use :undef internally.

#10 Updated by Andrew Parker about 1 year ago

We could do a transitionary period that converts undef to empty string and generate a warning. After a period of time change the warning to an error.

#11 Updated by Chris Price about 1 year ago

I agree about the general strategy of never exposing :undef outside of our walls… so, for this specific ticket, what we are talking about is changing the current behavior where we translate undefs to ‘’ before passing them to functions; we’d pass nil instead. As far as I can tell, everyone seems in relative agreement on that point.

Do we need to introduce a transition period with a deprecation warning? It would certainly be the safest path forward if we are concerned about this change breaking a lot of things, but it would postpone the ability for users such as the author of this ticket to actually handle this type of use case. So, the sooner we are able to introduce the change, the better.

Thoughts? Daniel / R.I. do you gentlemen have any ideas as to exactly how large of an impact this change would have on the community?

#12 Updated by R.I. Pienaar about 1 year ago

I think we’d def need a transition period which would have to incude us auditing the stdlib and shipped functions for bad assumptions about input.

It’s probably a terrible idea but we could catch NilClass style exceptions and recall the function with nils as “” for some transition period? I have no idea if this is a good idea, suspect not.

#13 Updated by Chris Price about 1 year ago

Added Jeff as a watcher—Jeff, any thoughts about how this would impact stdlib?

#14 Updated by Jeff McCune about 1 year ago

Chris Price wrote:

Added Jeff as a watcher—Jeff, any thoughts about how this would impact stdlib?

I’m not sure other than checking the spec tests.

I’ve never seen a function that returns undef but I totally see the value nd the need to be consistent with regard to casting arguments and return values.

#15 Updated by Jeff McCune about 1 year ago

R.I. Pienaar wrote:

I’m in favour of making undef behave like ruby nil in templates and functions, might be a painful transition but ultimately it means any beginners guide to Ruby actually applies to our code where right now almost every sysadmin who comes to Ruby via puppet finds it a rather painful experience.

+1

A criticism I often received when training people was that “Puppet is inconsistent”. I believe how we cast things to strings in functions, particularly undef values, is a major contributor to this perception.

This is absolutely not something we should change in a minor release and ideally we’ll have a full major release where we give a deprecation warning with clear steps to take for authors of functions and types.

#16 Updated by Chris Price about 1 year ago

  • Status changed from Needs Decision to Accepted
  • Target version set to 3.x

OK. I’m going to go ahead and just mark this ticket as accepted, and get the deprecation warning added for Telly. That will give us some lead time to figure out when we want to make the actual switch.

Thanks for all of the feedback.

#17 Updated by Chris Price about 1 year ago

  • Status changed from Accepted to Needs Decision

#18 Updated by Chris Price about 1 year ago

  • Status changed from Needs Decision to Accepted

#19 Updated by Chris Price about 1 year ago

After we get the deprecation warning in for Telly, we should create another ticket for handling the implementation later.

Also—might want to look for other places where :undef is being special-cased; it seems like the consensus is that we should probably change all of these instances, not just the case relating to function parameters.

#20 Updated by Chris Price about 1 year ago

  • Status changed from Accepted to Needs Decision

#21 Updated by Jonathan Harker about 1 year ago

Chris Price wrote:

I discussed this with Matt Robinson, who had been involved to some degree with the original ticket. His recollection was that there were arguments for both of the two approaches—so it does not sound like there was an overwhelming argument in support of the current behavior.

It seems to me that an important consideration here is: “what is the target audience of the function definition API?” If we’re targeting developers, then I don’t think it’s unreasonable to expect authors to know that they may need to account for nil or :undef values being passed in as arguments. If we’re targeting a broader audience and making the function definition as simple as possible is the primary goal, then I can perhaps see the case for passing undefs as empty strings.

My overall take on this, though, is that there are cases in the world where there is a meaningful semantic distinction between “null” and an empty string (plenty of precedent for this in relational databases, etc.), and that our current implementation explicitly prevents users from handling that distinction. Thus I’d be inclined to mark this ticket as accepted and propose that we change the behavior… probably to passing ‘nil’.

Will leave this ticket open for comments for a while and see if there are strong feelings one way or the other.

I think that, semantically, nil makes more sense than the empty string; but ideally undef shouldn’t be changed. I am running into an issue with hiera similar Timur. If I pass undef into hiera as a default value, I expect to get undef out on the other side, not the empty string. If I get nil back, that is easier to test for, but I don’t think that is something I should have to test for, it’s why I’m giving a default value.

#22 Updated by Chris Price about 1 year ago

That is a reasonable concern, thanks for mentioning it.

#23 Updated by Chris Price about 1 year ago

  • Assignee deleted (Chris Price)

#24 Updated by Jeff McCune 12 months ago

  • Status changed from Needs Decision to Duplicate

Action Required

We have gone back and forth on this a lot. At the very best we’re wildly inconsistent in 3.x and all of our values for undef internally evaluate to truth and internally have non-empty to_s implementations.

Based on the huge number of undef related tickets in the issue tracker, I’m making a stand and saying #15329 is the real root cause of all of these issues. If you are a watcher on this ticket, please transfer your most pressing concerns to #15329.

Here is the list of related “undef” issues that I’ve found so far: #4692 #5820 #6621 #6745 #8778 #8783 #13210 #14654 #14677 #14666

I am going to close this ticket as a duplicate of #15329. Again, please transfer your most pressing concerns to #15329 in an effort to consolidate all of the issues related to undef variables in the Puppet DSL.

Thanks, -Jeff

#25 Updated by eric sorenson 10 months ago

FWIW in 3.x as it stands, the sample code behaves as Timur expected:

[eric@glitch.local ~/Sandbox/puppet-undef/manifests]% puppet apply --modulepath=/Users/eric/Sandbox 13210-undef-back-from-functions.pp                                                                                                          Error: Duplicate declaration: Notify[x] is already declared in file /Users/eric/Sandbox/puppet-undef/manifests/13210-undef-back-from-functions.pp 
 at line 2; cannot redeclare on node

#26 Updated by Andrew Parker 8 months ago

  • Target version deleted (3.x)

#27 Updated by Raphaƫl Pinson 23 days ago

I know this bug is marked as closed, but I think there is a way to fix this without redefining ‘undef’ in Puppet

See the code in https://github.com/raphink/puppet/compare/dev;function_honor_undef, which essentially does this:

  • Define a new :honor_undef parameter when creating functions, which prevents :undef from being munged when passed as an argument to a function. This way, function authors can choose to manage :undef themselves instead of having it default to ‘’;
  • Use :honor_undef => true in the hiera() function, so ‘undef’ can be passed in Puppet as a default value.

Would that be an acceptable way to fixing this issue?

#28 Updated by Timur Batyrshin 23 days ago

Looks ok and thank you for the idea however I can’t say right now if it would have fixed the issue as I haven’t used puppet for nearly a year now.

Also available in: Atom PDF