Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
select another version of 'dist' into @INC
Perl
Branch: master

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
doc
lib/lib
t
.gitignore
README.pod

README.pod

NAME

lib::vswitch - select another version of 'dist' into @INC

SYNOPSIS

 # In the calling code
 use lib::vswitch BioPerl => '1.2.3';
 use Bio::AlignIO;

 # XXX: describe how it gets there

DESCRIPTION

This module adds to @INC the necessary directories for a specific version of a distribution (dist), so the modules inside may be loaded.

It also takes steps to ensure scripts loading multiple modules do not inadvertently attempt to use two different versions of a dist simultaneously.

The files for the specified version of the dist must have been installed already, in a particular way. XXX: This module does not yet help with that, but it should.

Avoid taking modules from two versions of a dist

Modules from one version of a dist may rightfully assume that other modules from the same dist are of the same version.

Maintaining the validity of this assumption is something lib::vswitch should do, and ad-hoc version switching may not.

When the dist is not already present on @INC, we simply add a dist no more than once. This is mediated by "%VSW" and "uselib".

Below are cases where the dist Foo has version 1 already available on @INC, and the caller requests to uselib(Foo = 2) >

A.

Module Foo::A was loaded from Foo v1. Version switch to dist Foo v2 makes a different version of Foo::A available.

B.

Module Foo::B1 was loaded from Foo v1. Version switch to dist Foo v2 makes available a Foo::B2 which expects Foo::B1 to load from Foo v2 also.

C.

Version switch to dist Foo v2 - no modules loaded from Foo yet.

Module Foo::C3 appears in both versions, but there is no problem because the newer one is found and the old one is shadowed. This is otherwise like Foo::A above.

If module Foo::C1 appears only in v1 and Foo::C2 appears only in v2 (modules added to or removed from the dist), it becomes possible to load either or both. Each could reasonably expect a version match with Foo:::C3, and the absence of the module from the other version.

For A and B, it was too late to vswitch, so we should refuse to try. For C, we must prevent the loading of modules from the shadowed dist.

Case A is simple to detect, though it needs some I/O.

Cases B and C requires the knowledge that Foo dist contains, in various versions, all of those modules; but without assuming that it includes the entire Foo:: namespace. This may be found by reading the .packlist if that is available, or for a vswitch-installed dist by scanning the tree.

Support monkey-patching

Mechanisms to prevent accidental mixing of modules from different versions may also make it harder to deliberately replace portions of the dist.

This is undesirable, because the maintenance of this code was already complicated before we got involved. Better would be for the code doing this to make it clear what is happening.

A likely solution is that the dist version being switched in has local edits applied: use lib::vswitch BioPerl => '1.2.3-patched'.

The "do not vswitch a dist twice" rule can be prevented by deleting from "%VSW". A neater way might be nice.

The "do not vswitch when the new tree contains an already-loaded module" rule may need an override. Possibly of the form use lib::vswitch Foo = 2, -force_loaded => qr{^Foo::A$} >.

RATIONALE

This is yet-another module messing with @INC. There should be a good reason.

Nearby I find several applications which depend on a specific version of some distributions of modules. These dependencies are maintained in several ways.

Alternatives

  • Write the app to assume the latest version of a module, within some range. Make this explicit in the use when there is a known bug.

    With this, version requirements increase gently upwards. Much of CPAN operatees in this way.

    Problems start when the new version of a module removes some feature and you lack the time to work around, re-implement or test for the new version; or you use a locally hacked version and the changes do not make it upstream.

    Once you are stuck on an old version, there might be no incentive to upgrade and it could require an increasing amount of work.

  • Write the app to use whichever version it finds itself presented with, in a backwards- or forwards-compatible way, using the documented replacements for deprecated calls etc..

    I mention this option for completeness. If your code was doing that, you wouldn't be reading this.

  • You can "roll your own" during app installation: install the version you need and make sure nobody upgrades it.

    We often do this, in various ways. It is quick and easy.

    When the application with the version requirement is a collection of modules (a "dist"), it may have callers which also need and provide the same module. Will they use the correct version? Would your code notice if it didn't?

    The use function doesn't provide for a "maximum version" so you are on your own at this point.

    There may be a perfectly usable copy of the modules you want already on @INC, but while you need to force the use of a known-version copy it will be difficult to take advantage of that.

  • Build something over Module::PortablePath and use tags of the form ensembl62 .

    These tags must be maintained (in our case, by the central authority via a helpdesk ticket). You need a new one for each version.

    What happens if a script brings in ensembl62 and then later adds ensembl63?

    How do you know when ensembl47 is no longer used?

  • Distributions which are well known to have dependent applications using specific versions could provide explicit support to do this.

    This could reduce the repetition down to approximately the number of dists used this way, but only where and when the dist authors care to add support.

In short, the problem can be managed but it takes some effort.

Problems to solve

From the list above, there are some common problems on the application side

  • Prevent the simultaneous import of two versions of the same dist.
  • Allow safe sharing of known-version dist install trees.

and more in the managing of installations of dists

  • It should be as easy to install another version as to install any other dist.
  • Installed versions should be enumerable.
  • It would be very useful to enumerate dependent applications. Preferably without waiting for them to be run, in order to hook the version switching call.

    XXX: Here the Module::PortablePath tag solution may be superior because it uses one symbol-shaped string to declare the dependency.

  • Bonus points for making the often-used set of known-version dists easy to install via CPAN.

Problems remaining

The freezing of module version requirements in otherwise-living code is itself a problem.

Will making it easier to manage the knock-on problems make that worse?

PACKAGE GLOBALS

%VSW

This hash is used to prevent the use of different versions of the same dist.

It is offered as part of the public API for this module to improve transparency. Keys are dist names, values are the versions set by "uselib".

Writing to it is likely to cause problems.

CLASS METHODS

These are not particularly intended for normal use by client code, but might form some useful building blocks.

find($dist, $vsn)

Return the path for the specified version of the named dist.

This is based on "class_file" in File::ShareDir and so should respect subclassing of lib::vswitch (XXX: untested).

uselib($dist, $vsn)

Discover the new path using "find", then ask lib to add it to @INC .

This checks and updates "%VSW" to prevent multiple uselib of the same dist.

import(...)

This does the work when you write something like

 use lib::vswitch 0.73 BioPerl => '1.2.3';
 # 0.73 would be a minimum version for lib::vswitch itself.
 
 use lib::vswitch 'Foo-Bar' => '0.05-hacked';

Currently it takes one ($dist => $vsn) pair and calls "uselib" with them.

Extra flags or the ability to uselib multiple pairs may be added later.

CAVEATS AND BUGS

There is no unimport via no. It is not clear that this could be done safely.

Something went wrong with that request. Please try again.