Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Everything you wanted to know about Module Version Numbers and Checking

by John M. Dlugosz (Monsignor)
on Aug 06, 2001 at 04:22 UTC ( [id://102368]=perltutorial: print w/replies, xml ) Need Help??

History

Those of us who learned pm stuff when Perl 5 came out remember that supplying a numeric argument to the import list will assert that the module is that required version. The docs used to state that this had hidden potential, of being able to sense and react to the caller's requested version, in case updates are not exactly compatible.

Times Change

However, this whole mechanism has changed. As of Perl 5.6.1, there is support for "v-strings" of the form v5.6.1. This raises immediate confusion because comparing v5.6.1 against '5.6.0' will not work, as the values are in totally different and incompatible formats. Since the single-argument for of require and use to demand a minimum Perl version will take either way, it might come as a surprise that your own code and module versions do not.

So, when assigning a value to $VERSION, do you use the old way or the new way? If there is a choice, the user must know the proper way to formulate numbers when refering to your module's version.

The Reality

The way module version checking works has changed. With Perl 5.6.0, use and require can take a version number as the "indirect object". In 5.6.0 this had to be a numeric literal; in 5.6.1 it can be a v-string.

indirect-object VERSION syntax

Instead of including the version number as an argument to import, now you are to write:
# use VersionTest qw/1.2.3 foo bar/; # the above is no longer Kosher use VersionTest v1.2.3 qw/foo bar/; # do it this way (no comma).
The thing between the module name and the list must be a v-string or numeric literal. Anything else is rejected by the parser, unlike the normal indirect-object syntax that can be overridden with braces even if it doesn't smell like an indirect object.

For example, you cannot write:

use VersionTest '1.2.3' qw/foo bar/; #string my $needed= v2.3.4; use VersionTest $needed qw/foo bar/; #not a literal

indirect-object VERSION check semantics

If you use this syntax, Perl calls the module's VERSION method with that value as the argument. If you write such a sub, it can do anything you want, presumably do more elaborate version range checks.

If you don't write a sub VERSION, then your module inherits one from UNIVERSAL::, and that does a simple ge test.

So, if you expect to use the indirect-object form of version checking, your $VERSION variable must be a v-string or a floating point value using the three digit convension, explained below. If you use a literal such as $VERSION= '2.30.4', the old way, then the UNIVERSAL::VERSION function croaks with an error that '2.30.4' is not numeric, pointing to your caller's use statement. Not very useful, since the value it mentions isn't found anywhere where the error is reported!

Perl 5.6.0 used numbers for versions with this feature, with floating-point numbers encoding the version with three decimal digits per part. The idea was that version X.Y.Z would be represented as X+Y/1000 + Z/1000000. So 2.3.4 is the number 2.003004. That's why you had Perl version 5.005.

This is important because UNIVERSAL::VERSION will allow mixing of v-strings with floats by converting the v-string to a float using this convention.

So, if the module defines $VERSION=1.2 and you use Module v1.2 it will compare 1.2 against 1.002, which is not what you meant. If you reverse that and declare $VERSION=1.2.1 (v is implied if there is more than one dot in a literal) and then use Module 1.1, you will learn that 1.2.1 is less than 1.1, since the former is converted to 1.002001. The module will refuse to load since it is not a high enough version!

The lesson here is that if using numbers, not v-strings, always remember the 3-digit convension. It is improper to make a module version 1.2 (a floating point literal), unless you really did mean v1.200. Some modules do this wrong, and will cause this kind of surprise.

version import number

So what of the "old way", of listing a number in the import list? E.g.
use VersionTest qw/1.2.3 foo bar/;
If you are using the normal Exporter::import function, this will call your module's require_version method. If you don't define one, you inherit one from Exporter. That implementation is surprising:

It will do a numeric comparison between your request and $VERSION. What's '1.2.3' going to do with a numeric comparison? It will parse the floating-point value 1.2 and ignore the rest. Basically, it only looks at the first two parts, and silently ignores everything else! This is different from the indirect-object version check, which gives a "not numeric" error instead.

Worse yet, it will see a v-string as a numeric zero. So, if you have a v-string in $VERSION and you use the import-number form, your version check will always fail but no testing had been done!

Hmm, an old program uses this technique, and the old module had $VERSION='1.2'. The new module has $VERSION=v2.0.1, and you run your old program. It will report that you asked for 1.2 but the file contained version 0.0, and it won't load.

Where are we now?

Using the supplied functions as inherited from Exporter and UNIVERSAL, the old and new forms of module version checking are fundimentally incompatible.

If you change from old to new when updating a module, and intend it to be backward compatible, it won't be if a user checks the version! New code, though, is expected to use the new form, isn't it?

With two incompatible forms, the user would have to know which to use, on a per-module basis. It's easy to figure out because doing it the wrong way results in a run-time error during the version checking code.

An easy solution would be to supply a require_version method that also uses the v-string in $VERSION but expects a old-style string as an argument. Do this with something similar to $requested= eval "v$requested"; VERSION($requested);

Survey

Socket.pm defines $VERSION="1.72". Being a string, the indirect-object syntax will be rejected. For the import-list syntax, it will be taken as a floating point number, and the user needs to understand that it's a single float, not a dotted pair, when comparing them. The quotes seem like a mistake knowing that the importer compares numbers not dotted strings, but look at what it does: it prevents the new syntax from being used. That can be a way to keep backward compatibility with old modules.

Symbol.pm says $VERSION = 1.02, which does not follow the 3-digit convension. This will give improper comparisons when using the indirect-object syntax.


—EOF—

Replies are listed 'Best First'.
Re: Everything you wanted to know about Module Version Numbers and Checking
by Anonymous Monk on Aug 06, 2001 at 09:55 UTC
    I am sorry to say that this is not as helpful as the title had seemed to promise.

    You begin and end with questions and conundrums. That is not helpful in a tutorial. A tutorial should provide practical help to the naive but eager reader.

    In the third paragraph you pose the key question: do you use the old way or the new way? Then you spend the rest of the post talking around but not answering that key question. I have read this through several times and I do not find that answer.

    As I read your 'tutorial' and think about my expectations, I am aware that tutorials usually have one of two forms (either as a whole, or sub-section by sub-section). Either:

    • Problem
    • Solution
    • Discussion
    or...
    • Problem
    • Discussion
    • Solution

    Your solution section seems to be missing. You have illuminated the problem quite well -- and that is worth something. But sadly, after reading your post, I find myself even more discouraged in my search for the proper or best solution.

      I'll edit it to point to a module containing an implementation of a "good" solution, after I finish writing one.

      I'm sorry that the state of the language features is inconsistant. That's what's out there to know!

      This was originally posted in Meditations, where I collected comments and corrections for a week before posting the final version here. Nobody had any "good news" with regards to this feature.

      —John

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perltutorial [id://102368]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2024-03-19 04:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found