John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

Normally, if you inherit from Exporter, it will process the import arguments against the @EXPORTS etc, and if given a number will verify that $VERSION is good enough.

I'd like to override this so that a version number specified on the import args will enable backward compatibility. In the general case, this means (1) setting another package variable to the requested version, so various functions can check that at their lesure; and (2) change what's imported by default (same effect as a different @EXPORTS list).

What's the best/easist/proper way to do that?

(Meanwhile, as for point 1, that should actully be a hash noting which packages asked for which versions, but that's a fine detail.)

If no version number is given in the import list, it should also enable the backward-compatibily mode. So, it needs to check for that first, before calling the super import.

Idea at this point is to scan for version number in the arglist, delete it if found, do a local @EXPORTS to reflect the backversion and then call super's import.

But open to suggestions.

—John

Replies are listed 'Best First'.
Re: Overriding Exporter::import
by bikeNomad (Priest) on Jul 19, 2001 at 07:47 UTC
    You can override the require_version() routine in your Exporter-derived modules. It gets passed the version. See the Exporter manpage, in the section labeled Module Version Checking.

      This method will usually not get called. Quoting "perldoc -f use":

      If the VERSION argument is present between Module and LIST, then the `use' will call the VERSION method in class Module with the given version as an argument. The default VERSION method, inherited from the UNIVERSAL class, croaks if the given version is larger than the value of the variable `$Module::VERSION'.
      The Exporter::require_version is only a fall-back for mistakes like: use My::Module qw( 5.5 MyFunc ); which should really be written as: use My::Module 5.5 qw( MyFunc ); Note how the version number doesn't have a comma after it.

      So you need to have your module do some "sub VERSION" if you want to subvert the &UNIVERSAL::VERSION version of the sub.

              - tye (but my friends call me "Tye")
        That's a relativly new feature, isn't it? I don't recall ever seeing the "indirect object" for use before.

        The perldoc you mentioned seems to be reciently updated, since it's not entirely consistant. It still says it's exactly the same as BEGIN { require Module; import Module LIST; } but doesn't mention VERSION. It should say BEGIN { require Module; Module::VERSION (VERSION); import Module LIST; } which is confusing because VERSION is used both as a actual token and as a placeholder, so whoever edits that should make other changes too.

        Who maintains the perldocs? I'd like to submit a correction or at least inform the proper person.

        —John

Re (tilly) 1: Overriding Exporter::import
by tilly (Archbishop) on Jul 20, 2001 at 01:12 UTC
    Beware. Exporter uses caller to figure out where to export things to. If you want to override it, you can but you need to erase yourself from the call stack with goto.

    I have an example of how to do it at Versioned modules.

    There are some documented ways to do it with Exporter. However when I looked at it about a year ago, it turned out that they were somewhat buggy. (Something about unused code not getting maintained, and other code expecting how many layers back to look for caller. I don't remember details though.) The goto trick is not.

      re beware of caller:

      The normal import calls that internal function which is told how many levels back to go, so I think if used carefully it won't be any more buggy than normal :)

      If I localize @EXPORT and then do a magic call-stack goto, what happens? If it transfers localization to the new routine as well, I can easily jump to the "super" form without having to muck with the internal forms.

      —John

        When you localize something and then goto, it first has to clean up your scope before removing it from the callstack and so the local is lost.

        The documented approach if you are not calling Exporter's import directly is to call export_to_level. It is buggy in at least some versions of Perl with some exports. (I think it is fine if you are just exporting functions, but if you trigger Export::Heavy...) No, I don't remember exact details, but I do have a recollection floating around that it had to do with the rewrite for Exporter/Heavy.pm. You will have to analyze the source code for yourself if you want to know more.