xdg has asked for the wisdom of the Perl Monks concerning the following question:

I have code that depends on Scalar::Util::weaken, which is only available if the XS version of Scalar::Util is installed. I'd like to gracefully detect if weaken isn't available and handle that in a sane way. That's easy enough to do.

My challenge is this: how do I test that error handling if I have the XS version installed? What is a limited way to test what happens when an XS version of a module is not available? How can I prevent the XS from being loaded for one particular module? (Without having a whole separate Perl installation.)

One idea I had was to override DynaLoader::bootstrap to just die -- which causes Scalar::Util to note the failure to bootstrap and likewise die with an appropriate warning about weaken not being available.

use strict; use warnings; use Test::More tests => 1; BEGIN { eval{ require DynaLoader }; no warnings 'redefine'; *DynaLoader::bootstrap = sub { die }; } eval "use Scalar::Util qw( weaken )"; like( $@, qr/\AWeak references are not implemented/, "Catch unavailable weaken" );

(If necessary, this could be more targeted by checking which module is being bootstrapped and dying or going to the original bootstrap as appropriate.)

Is this safe? Sane? Have I overlooked some easier way of doing this? Is there some module on CPAN that makes this easier?

Feedback greatly appreciated.

update: The same technique applies for XSLoader::load.

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Replies are listed 'Best First'.
Re: Breaking XS for testing? (@INC code ref)
by tye (Sage) on Mar 06, 2006 at 22:40 UTC

    You could replace @INC with a single code ref that rejects attempts to require certain packages, otherwise it searches @INC itself. Note that this answer demonstrates that I know that you can put a code ref into @INC but that I don't know if such code refs can prevent subsequent entries from being search or if you can temporarilly override @INC and re-require within the ref'd code.

    - tye        

      The problem with that is that I still want the pure-Perl part of Scalar::Util to work. My test file was just a idealized case to show that it can be done.

      In the real case, I want to load parts of Scalar::Util that have Perl fall-backs, try to load weaken, detect if it fails, and then have a sensible response (in this case, issuing a warning and continuing). So I need to interrupt XS loading only, without affecting @INC. Otherwise, I'd probably have gone tried that.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        I was assuming a sane "fallback if no XS version available" scheme such as I've seen many times, where something like Scalar::Util_XS is a separate module that is only optionally installed. But Scalar::Util doesn't use this level of sanity and instead implements a lot of special installation machinery in order to allow it to install just parts of itself, so you have to learn its own flavor of magic in order to deal with it.

        It looks like overriding DynaLoader::bootstrap() would suffice:

        require Dynaloader; my $orig= \&DynaLoader::bootstrap; *DynaLoader::bootstrap= sub { ... };

        - tye        

Re: Breaking XS for testing? -- Test::NoXS now on CPAN
by xdg (Monsignor) on Mar 10, 2006 at 21:17 UTC

    In case this is useful for others, I've put it on CPAN as Test::NoXS.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.