Clear questions and runnable code get the best and fastest answer |
|
PerlMonks |
RFC: Preventing a module from loadingby Corion (Patriarch) |
on Feb 16, 2003 at 11:41 UTC ( [id://235707]=perlmeditation: print w/replies, xml ) | Need Help?? |
I like modules that provide a dynamic fallback and degrade gracefully if some prerequisites are not available instead of requiring modules when they can do well without them. But there is a problem - on my development machine, I have all these optional modules installed, but I want to test the behaviour of my code without the optional modules. So I want to set up tests where the optional modules seem not available. My preferred syntax for this is a pragma-like syntax :
So, most of the magic will have to be installed in a sub called "import()" within my (to be written) module. When you want to muck around with module loading, the only way in Perl seems to be to add a code reference into @INC. That code reference either returns a filehandle, from which the text will be loaded, or undef, which means that the next entry in @INC will be tried. Things that didn't work :
This first variant worked quite well, until I came up to Digest::MD5, which wants to load XS code. And the XS code loader looks through @INC, it dosen't respect coderefs in @INC, and thus, the loading of Digest::MD5 fails. Or rather, Digest::MD5 has a fallback to Digest::Perl::MD5, which I didn't have installed. So this way will not work as soon as we use any module which uses XS code. So I had to keep all existing directories in @INC, but there was no way to prevent Perl to look through the rest of @INC if my handler returned undef for a blocked module :
demerphq then suggested that I forget about a handler in @INC and muck instead with %INC and a custom import method, that would die whenever that module was imported into a new namespace.
But this version didn't work, because one could still require the module, and most checks whether a module is available rely on the meme
But this put me on the right track, I would simply create a faked module on the fly, and return this faked module whenever I want to prevent a module from loading. I don't need to handle the case that a module is allowed, as the rest of @INC will take care of that.
There are now some technical pitfalls. First, IO::String does not work in an @INC-handler, seemingly Perl wants a real filehandle (or at least, Acme::Intraweb and PAR do it that way as well), so I have to create a tempfile for every faked module. That's not a real concern as my module is intended for testing anyway - efficiency is of no importance. Second, what if a module has already been loaded? Then Perl won't go through @INC at all. So we have to scrub %INC as well and clean it of the unwanted modules, in case they have already been loaded. After these tries, the algorithm to prevent a module from loading now looks like the following :
The complete module is appended below. If you have suggestions about the naming convention or the usage interface, I'd like to hear about them. If you have any hint on how to make my module into a lexical pragma (warnings.pm and strict.pm didn't offer a hint to me), I'll be even more interested.
Update: Fixed calling of scrub. That's what you get for last minute changes !
Back to
Meditations
|
|