Real $VERSIONs on CPAN

I've been looking at patterns of $VERSION definition in the wild. And, wow. There's some crazy stuff out there.

Remember that the way PAUSE (and other tools) parse a $VERSION definition line is not by loading the module, but by extracting it as a standalone line and essentially running it through eval. PAUSE says this must work:

perl -MExtUtils::MakeMaker -le 'print MM->parse_version(shift)' path/to/file.pm

PAUSE actually does something more sophisticated and safe, but if parse_version works, so will PAUSE.

Keeping that in mind, listed below are some of the more surprising/amusing/horrifying things I've seen. Not all are broken, but even the ones that work are, well, unusual.

I'm not naming any names — and I'll obscure them if it would be too revealing — but these are all from real .pm files in tarballs currently indexed on CPAN.

$VERSION = '';

$VERSION = '0.O1';

$VERSION = "0.01c";

$VERSION = VERSION;

$VERSION = '0.10E0';

our $VERSION = -722;

my $VERSION = '0.01';

$VERSION = 0xdeadbeef;

our $VERSION = '1.0-1';

$VERSION = 2006_08_16.1;

our $VERSION = '0.8.1-2';

$VERSION = $VERSION = "0.1";

$Foo::Bar::VERSION |= '2.6';

my $VERSION = 'OMG-04-05-01';

our $VERSION = '1.4.A8UG1gG';

our $VERSION = 'set-when-loading';

our $VERSION = '$Revision: 1.2 $';

our $VERSION = $Foo::Bar::VERISON;

our $VERSION=$Foo::VERSION; use Foo;

local $Other::Module::VERSION = 666;

$Foo::Bar::VERSION="1.23" unless $Foo::Bar::VERSION;

$Foo::Bar::VERSION='1.00' unless $INC{'Foo/Bar0.pm'};

$VERSION=eval 'use version;1' ? 'version'->new('0.33') : '0.33';

Some of these are so nuts that unless they are modified by a subsequent line into a standard version format, then a version check will throw an error on any recent perl:

# Foo.pm
package Foo;
our $VERSION = "1.2-trailing-junk";
$ perl -I. -we 'use Foo 0;'
Invalid version format (non-numeric data) at -e line 1.
BEGIN failed--compilation aborted at -e line 1.

Moral of this story: make sure your $VERSION definition parses cleanly on a line by itself and make sure it's a valid version number. If you're not sure, check it with the is_lax function from version.pm.

This entry was posted in perl programming and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

10 Comments

  1. Ether
    Posted October 9, 2013 at 8:27 pm | Permalink

    It's too bad that PAUSE doesn't refuse to index (or at least respond with a warning for) any distributions with invalid versions. If the error was visible, I'm sure more people would attempt to comply.

  2. Posted October 10, 2013 at 11:09 am | Permalink

    Too bad more people don't use or recommend Test::Version

  3. Tom Christiansen
    Posted October 10, 2013 at 6:44 pm | Permalink

    What, you mean everybody doesn't use this? :)

    our($VERSION) = q($Revision: 1.8 $) =~ / \b ( \d+ (?: \. \d+ )+ ) \b /x;

  4. James E Keenan
    Posted October 13, 2013 at 9:34 am | Permalink

    So let's take as given that we didn't want to publicly identify the errant distributions or authors. Would it be worthwhile to consider an email campaign of advisories concerning up-to-date $VERSION practices?

    • Posted October 13, 2013 at 10:35 am | Permalink

      I think it's a relatively small handful of things so I wouldn't go to the trouble of bothering authors about historical modules.

      I prefer ether's suggestion of having PAUSE either tighten up what it allows or at least warning people more aggressively when new uploads happen.

      And I wouldn't object at all to a Kwalitee test. Just because I didn't want to name names here doesn't mean it can't happen in some other forum.

  5. Posted October 13, 2013 at 2:15 pm | Permalink

    Now you confused me even more. Which one should we use?

    • Posted October 13, 2013 at 2:34 pm | Permalink

      The examples are all bad. I strongly recommend:

      our $VERSION = "YOUR_VERSION";

      Where "YOUR_VERSION" is a string that passes version::is_lax("YOUR_VERSION"). Even better is if it passes version::is_strict("YOUR_VERSION").

      For code that requires v5.12 or later, this is the best because it enforces strictness and thus (assuming your code compiles at all) can later be parsed without an "eval":

      
      use v5.12;
      
      package Foo::Bar YOUR_VERSION;
      
  6. Bruce Van Allen
    Posted October 14, 2013 at 10:21 am | Permalink

    Why would this:

    package Foo::Bar;
    our $VERSION = "YOUR_VERSION";

    be preferred over this?

    package Foo::Bar;
    $Foo::Bar::VERSION = "YOUR_VERSION";

  7. Posted October 14, 2013 at 12:27 pm | Permalink

    Less to type?

  8. Ether
    Posted October 15, 2013 at 2:00 pm | Permalink

    @Bruce: I've seen the former play out better depending on how the module is loaded -- e.g. see http://stackoverflow.com/q/17705402/40468 and https://rt.cpan.org/Ticket/Display.html?id=86611

    It looks like charsbar (Kenichi Ishigaki) didn't have much sleep this weekend, as he's already churned this out: http://cpants.cpanauthors.org/kwalitee/has_proper_version \o/

© 2009-2014 David Golden All Rights Reserved