I have an interesting situation. In some (large) legacy code, there is a namespace that should look like require A::B, but instead the path to A was added so it's possible to just say require B. However, I would like to be able to use both invocations. Is this possible without creating a redirecting package? For instance, is there a way to dual declare a package?
Thanks!
First load the package:
require A::B;
Then alias B to A::B:
*B:: = *A::B::;
Then tell require that it has already loaded B
$INC{'B.pm'}++;
To make sure this all works right, it is best to perform these actions inside a BEGIN block:
BEGIN {
require A::B;
*B:: = *A::B::;
$INC{'B.pm'}++;
}
After this, all require A::B; and require B; lines will become no-ops. You will also be able to refer to variables in that package with either name. \&A::B::foo == \&B::foo
To get this to work transparently, you could add the following code to each file:
A/B.pm
*B:: = *A::B::;
$INC{'B.pm'}++;
B.pm
*A::B:: = *B::;
$INC{'A/B.pm'}++;
Then if a user does require A::B; they can call A::B::foo or B::foo and require B; will become a no-op.
And if a user does require B; they can call A::B::foo or B::foo and require A::B; will become a no-op.
But for maintainability, it is probably best to keep all of the real code in one file (along with the aliasing code above), and setup the other file as a shim that just loads the real file. Assuming A/B.pm contains the real code:
A/B.pm
*B:: = *A::B::; # this gets added to the existing file
$INC{'B.pm'}++;
B.pm
require A::B; # this is the entire file
require Something will search the directories in #INC for a file called Something.pm.
To get some-path/A/B.pm to be loaded with either require B or require A::B, you would need to have both some-path and some-path/A in your #INC directory list.
There are many many ways to add directories or otherwise manipulate your #INC list.
Eric's solution would work, but truthfully, I'd shoot anyone who did this in real production code. You could probably achieve similar results by using methods in Package::Stash, but again, why mess up the symbol table like this? I'd rather fix the legacy code that was calling things the wrong way. Seriously, how hard is it to do a search-and-replace on your code and fix the package names?
Another quick and dirty method to get require B; to actually find package A::B is to simply make a symlink in the lib/ directory pointing to A/B.pm:
cd lib
ln -sf -T A/B.pm B.pm
Note that this will create two packages with identical code, so variables in one will not be the same value as the other: $A::B::foo will be entirely separate from $B::foo even though their declaration comes from the same file.
Related
How do I load a module with a path not in the #INC in Perl?
I've read the answers on how to add a path to the #INC, but the issue is that #INC has to be changed at the beginning of the code. After it's compiled, all the modules I've come across look to #INC to find the location of the module. My issue is that we're trying to separate out all these environment specific things to a config file. If it's in a config file, the path needs to be read before it can be pushed to #INC, but then the code has already been compiled and it seems #INC can't be modified.
Is there a way? Is there a library that lets me load a module and pass it a custom path?
Is this a terrible bad thing to do? Why?
Perl has an incremental compilation model which means that code can be executed before other code is even parsed. For this, we can use phase blocks (aka. phasers):
BEGIN {
print "This is executed as soon as the block has been parsed\n";
}
Such a phase block could also be used to load a configuration file.
For example, use statements are effectively syntactic sugar for a BEGIN block.
use Foo::Bar qw/baz qux/;
is equivalent to
BEGIN {
require Foo::Bar; # or: require "Foo/Bar.pm";
Foo::Bar->import(qw/baz qux/);
}
We can also load modules at runtime, although that's only sensible for object-oriented modules.
So we have three options:
Load config in the BEGIN phase and add the correct library paths before loading the actual modules
Load the modules manually during BEGIN with their full path (e.g. require "/my/modules/Foo/Bar.pm"
Figure out the configuration at runtime, load modules after that.
Using bare require is fairly uncomfortable, which is why Module::Runtime exists
Use a BEGIN block to load your custom #INC location and then use lib to include it.
# use lib /a special directory/
BEGIN {
my $lib_to_include = ...;
require lib;
lib->import($lib_to_include);
}
use Module_That_Requires_Special_Dir;
The only thing to note is that whatever code you use to load your custom include directory will have to rely on methods already defined before this BEGIN block. Therefore you can't use a subroutine that is later in the file.
Came across only, which seems to let a path be passed to the use argument like so:
use only { versionlib => '/home/ingy/modules' },
MyModule => 0.33;
It has to be used with a version condition, but putting this here anyways since it's relevant to my question 1 and I wasn't able to find any modules first time around that allowed a path outside #INC.
require supposedly is able to take in a full path, according to the perlfaq with
require "$ENV{HOME}/lib/Foo.pm"; # no #INC searching!
We have a big Perl based Web site.
I am assigned to refactor code of many scripts and packages. Sometimes changes are easy and I just modify existing functions. But sometimes I need to rewrite entire functions. The bad news that the functions which I rewrite call other functions. So if I move refactored code in my new module I need also copy all supplementary functions. But if I do not move refactored code to my special module, a tiny syntax error may crash the entire site :-(
Yes, I know we should use version control, etc. But we don't and this is a fact which I can't change. What to do?
So I need to keep some code in a Test module (to avoid syntax errors to crash the entire site). Would it be OK to make circular references from other modules to Test (for my new refactored routines) and from Test to other modules (for supplemetary routines)?
Note that we have some AutoRequire module which is required by most of our scripts and modules. AutoRequire makes A::X() call to automatically load A module (if it is not yet loaded).
My main question is whether it is OK in this settings to use mutual module dependencies. Any other suggestions?
Require works by first checking if the module is already loaded and if not load it and afterwards mark it as loaded. So if you have a dependency that Foo requires Bar and Bar requires Foo it will first try to load Foo, within Foo try to load Bar. But because Bar requires Foo and Bar was not yet finally loaded this can give problems.
Edit after input from #ysth: it will not fail but it might load some thing only partially, which might cause interesting problems later.
So it might just work, but it also might give problems later (like failing to use exported functions or so).
Perl allows mutual dependencies, but they can sometimes lead to unintuitive behaviour. An example I've given before is:
foo.pm
package foo;
use bar;
sub import { printf("%s loaded foo\n", scalar caller) }
1;
bar.pm
package bar;
use foo;
sub import { printf("%s loaded bar\n", scalar caller) }
1;
script.pl
#!/usr/bin/env perl
package main;
use foo;
Output:
foo loaded bar
main loaded foo
Didn't you expect to see bar loaded foo somewhere in that output?
With careful consideration of compile time versus runtime, etc, etc, you'll realise why that line was not output, and that this is proper and documented behaviour. Just not necessarily very intuitive.
That said, for purely object-oriented modules which don't do anything on import (e.g. export anything to their caller, or act as a pragma, or—as in this example—print output), it should generally be pretty safe.
That said, for purely object-oriented modules there's usually no reason to load them upfront with use anyway—instead you can load them with require or Module::Runtime when they are needed.
I have been USEing .pm files willy-nilly in my programs without really getting into using packages unless really needed. In essence, I would just have common routines in a .pm and they would become part of main when use'd (the .pm would have no package or Exporter or the like... just a bunch of routines).
However, I came across a situation the other day which I think I know what happened and why, but I was hoping to get some coaching from the experts here on best practices and how to resolve this. In essence, should packages be used always? Should I "do" files when I just want common routines absorbed into main (or the parent module/package)? Is Exporter really the way to handle all of this?
Here's example code of what I came across (I won't post the original code as it's thousands of lines... this is just the essence of the problem).
Pgm1.pl:
use PM1;
use PM2;
print "main\n";
&pm2sub1;
&pm1sub1;
PM1.pm:
package PM1;
require Exporter;
#ISA=qw(Exporter);
#EXPORT=qw(pm1sub1);
use Data::Dump 'dump';
use PM2;
&pm2sub1;
sub pm1sub1 {
print "pm1sub1 from caller ",dump(caller()),"\n";
&pm2sub1;
}
1;
PM2.pm:
use Data::Dump 'dump';
sub pm2sub1 {
print "pm2sub1 from caller ",dump(caller()),"\n";
}
1;
In essence, I'd been use'ing PM2.pm for some time with its &pm2sub1 subroutine. Then I wrote PM1.pm at some point and it needed PM2.pm's routines as well. However, in doing it like this, PM2.pm's modules got absorbed into PM2.pm's package and then Pgm1.pl couldn't do the same since PM2.pm had already been use'd.
This code will produce
Undefined subroutine &main::pm2sub1 called at E:\Scripts\PackageFun\Pgm1.pl line 4.
pm2sub1 from caller ("PM1", "PM1.pm", 7)
main
However, when I swap the use statements like so in Pgm1.pl
use PM2;
use PM1;
print "main\n";
&pm2sub1;
&pm1sub1;
... perl will allow PM2.pm's modules into main, but then not into PM1.pm's package:
Undefined subroutine &PM1::pm2sub1 called at PM1.pm line 7.
Compilation failed in require at E:\Scripts\PackageFun\Pgm1.pl line 2.
BEGIN failed--compilation aborted at E:\Scripts\PackageFun\Pgm1.pl line 2.
So, I think I can fix this by getting religious about packages and Exporter in all my modules. Trouble is, PM2.pm is already used in a great number of other programs, so it would be a ton of regression testing to make sure I didn't break anything.
Ideas?
See my answer to What is the difference between library files and modules?.
Only use require (and thus use) for modules (files with package, usually .pm). For "libraries" (files without package, usually .pl), use do.
Better yet, only use modules!
use will not load same file more than once. It will, however, call target package's import sub every time. You should format your PM2 as proper package, so use can find its import and export function to requestor's namespace from there.
(Or you could sneak your import function into proper package by fully qualifying its name, but don't do that.)
You're just asking for trouble arranging your code like this. Give each module a package name (namespace), then fully qualify calls to its functions, e.g. PM2::sub1() to call sub1 in package PM2. You are already naming the functions with the package name on them (pm2sub1); it is two extra characters (::) to do it the right way and then you don't need to bother with Exporter either.
How can I perform a "shallow" syntax check on perl files. The standard perl -c is useful but it checks the syntax of imports. This is sometimes nice but not great when you work in a code repository and push to a running environment and you have a function defined in the repository but not yet pushed to the running environment. It fails checking a function because the imports reference system paths (ie. use Custom::Project::Lib qw(foo bar baz)).
It can't practically be done, because imports have the ability to influence the parsing of the code that follows. For example use strict makes it so that barewords aren't parsed as strings (and changes the rules for how variable names can be used), use constant causes constant subs to be defined, and use Try::Tiny changes the parse of expressions involving try, catch, or finally (by giving them & prototypes). More generally, any module that exports anything into the caller's namespace can influence parsing because the perl parser resolves ambiguity in different ways when a name refers to an existing subroutine than when it doesn't.
There are two problems with this:
How to not fail -c if the required modules are missing?
There are two solutions:
A. Add a fake/stub module in production
B. In all your modules, use a special catch-all #INC subroutine entry (using subs in #INC is explained here). This obviously has a problem of having the module NOT fail in real production runtime if the libraries are missing - DoublePlusNotGood in my book.
Even if you could somehow skip failing on missing modules, you would STILL fail on any use of the identifiers imported from the missing module or used explicitly from that module's namespace.
The only realistic solution to this is to go back to #1a and use a fake stub module, but this time one that has a declared and (as needed) exported identifier for every public interface. E.g. do-nothing subs or dummy variables.
However, even that will fail for some advanced modules that dynamically determine what to create in their own namespace and what to export in runtime (and the caller code could dynamically determine which subs to call - heck, sometimes which modules to import).
But this approach would work just fine for normal "Java/C-like" OO or procedural code that only calls statically named predefined public subs, methods and accesses exported variables.
I would suggest that it's better to include your code repository in your syntax check. perl -I/path/to/working/code/repo/local_perl/ -c or set PERL5LIB=/path/to/working/code/repo/local_perl/ prior to running perl -c. Either option should allow you to check against your working code, assuming you have it in a directory structure similar to your live code.
I guess you could make stubs for the missing libraries in your home folder.
Have you looked into PPI? I think it does follow imports, however it could perhaps be more easily modified to guess what looks like a function name.
I am totally new to Perl/Fastcgi.
I have some pm-modules to which will have to add a lot of scripts and over time it will grow and grow. Hence, I need a structure which makes the admin easier.
So, I want to create files in some kind of directory structure which I can include. I want the files that I include will be exaclty like if the text were written in the file where I do the include.
I have tried 'do', 'use' and 'require'. The actual file I want to include is in one of the directories Perl is looking in. (verified using perl -V)
I have tried within and outside BEGIN {}.
How do I do this? Is it possible at all including pm files in pm files? Does it have to be pm-files I include or can it be any extension?
I have tried several ways, included below is my last try.
Config.pm
package Kernel::Config;
sub Load {
#other activities
require 'severalnines.pm';
#other activities
}
1;
severalnines.pm
# Filter Queues
$Self->{TicketAcl}->{'ACL-hide-queues'} = {
Properties => {
},
PossibleNot => {Ticket => { Queue =>
['[RegExp]^*'] },
},
};
1;
I'm not getting any errors in the Apache's error_log related to this. Still, the code is not recognized like it would be if I put it in the Config.pm file.
I am not about to start programming a lot, just do some admin in a 3rd party application. Still, I have searched around trying to learn how it works with including files. Is the severalnines.pm considered to be a perl module and do I need to use a program like h2xs, or similar, in order to "create" the module (told you, totally newbie...)?
Thanks in advance!
I usually create my own module prefix -- named after the project or the place I worked. For example, you might put everything under Mu with modules named like Mu::Foo and Mu::Bar. Use multiple modules (don't try to keep everything in one single file) and name your modules with the *.pm suffix.
Then, if the Mu directory is in the same directory as your programs, you only need to do this:
use Mu::Foo;
use Mu::Bar;
If they're in another directory, you can do this:
use lib qw(/path/to/other/directory);
use Mu::Foo;
use Mu::Bar;
Is it possible at all including pm files in pm files?
Why certainly yes.
So, I want to create files in some kind of directory structure which I can include. I want the files that I include will be exaclty like if the text were written in the file where I do the include.
That's a bad, bad idea. You are better off using the package mechanism. That is, declare each of your module as a separate package name. Otherwise, your module will have a variable or function in it that your script will override, and you'll never, ever know it.
In Perl, you can reference variables in your modules by prefixing it with the module name. (Such as File::Find does. For example $File::Find::Name is the found file's name. This doesn't pollute your namespace.
If you really want your module's functions and variables in your namespace, look at the #EXPORT_OK list variable in Exporter. This is a list of all the variables and functions that you'd like to import into your module's namespace. However, it's not automatic, you have to list them next to your use statement. That way, you're more likely to know about them. Using Exporter isn't too difficult. In your module, you'd put:
package Mu::Foo;
use Exporter qw(import);
our EXPORT_OK = qw(convert $fundge #ribitz);
Then, in your program, you'd put:
use Mu::Foo qw(convert $fundge #ribitz);
Now you can access convert, $fundge and #ribitz as if they were part of your main program. However, you now have documented that you're pulling in these subroutines and variables from Mu::Foo.
(If you think this is complex, be glad I didn't tell you that you really should use Object Oriented methods in your Modules. That's really the best way to do it.)
if ( 'I want the files that I include will be exactly like if the text were written in the file where I do the include.'
&& 'have to add a lot of scripts and over time it will grow and grow') {
warn 'This is probably a bad idea because you are not creating any kind of abstraction!';
}
Take a look at Exporter, it will probably give you a good solution!