Skip Menu |

This queue is for tickets about the Class-Autouse CPAN distribution.

Report information
The Basics
Id: 18460
Status: open
Priority: 0/
Queue: Class-Autouse

People
Owner: Nobody in particular
Requestors: mschwern [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: Unimportant
Broken in: 1.24
Fixed in: (no value)



Subject: "use lock" doesn't work... which is good.
I noticed this bit of code in Class::Autouse... # Add the AUTOLOAD hook and %INC lock to prevent 'use'ing *{"${class}::AUTOLOAD"} = \&_AUTOLOAD; $INC{$file} = 'Class::Autouse'; which worried me because the code base I'm applying Autouse to has hybrid modules, they're both classes and export functions. Its a large, crufty code base and not what I prefer, but I can't change that now. If the feature above worked it would prevent this sort of thing... Class::Autouse->autouse("Some::Thing"); use Some::Thing qw(function); Fortunately it doesn't work. :) Here's a simple example. #!/usr/bin/perl -wl use strict; use Class::Autouse; Class::Autouse->autouse('Foo'); use Foo; print $INC{"Foo.pm"}; print Foo::thing(); where Foo.pm is package Foo; sub thing { 42 } 1; Autouse's attempt to prevent loading is defeated, I suspect, because "use" calls import() which triggers the loading of the module. However, require is defeated. This breaks certain techniques... #!/usr/bin/perl -wl use strict; use Class::Autouse; Class::Autouse->autouse('Foo'); require Foo; Foo->import; print $INC{"Foo.pm"}; print thing(); Where Foo.pm is package Foo; use base qw(Exporter); use vars qw(@EXPORT); @EXPORT = qw(thing); sub thing { 42 } 1; Comment out the autouse() and it works fine. Put it back and thing() is undefined. I'm not sure why. If you go in with the debugger you'll notice Foo->import is going directly into Exporter->import rather than Autouse's loader even though the real Foo.pm remains unloaded. I don't know why this is. Anyhow, as the "use lock" is not doing its intended job and reduces the transparency of Autouse in this case, I'd recommend removing it.
Subject: Re: [rt.cpan.org #18460] "use lock" doesn't work... which is good.
Date: Fri, 31 Mar 2006 14:21:16 +1000
To: bug-Class-Autouse [...] rt.cpan.org
From: Adam Kennedy <adam [...] phase-n.com>
Oops, that is a typo in the comment. The purpose is to prevent... use Foo; ... from actually loading anything, and to return silently. Class::Autouse was written with a specific trade-off in mind. Most of the other autoloading modules attempt to do autoloading while retaining some capability of allowing ->import. Most of the magic of Class::Autouse, as well as the reason for its namespace, is that it trades off any support for things that aren't pure classes. That is, you don't get to use ->import, and in exchange it will make like SO much more simple. Class::Autouse is fundamentally incompatible with anything that needs to do more than BEGIN { require Module::Name; } It's something of a "have your cake and eat it too" scenario. There's no reasonable way I'm aware of to resolve the problems... I tried to make the documentation quite explicit about this limitation, and so you might want to consider another autoloading module that does support ->import. Or alternatively, consider loading the hybrid modules specifically in advance, and then apply Class::Autouse to the rest. That said, it may well be that I can make things degrade a bit more elegantly that I currently do, or provide better error messages, or something to enhance the usability in your situation, so I'll leave this bug open. Michael_G_Schwern via RT wrote: Show quoted text
> Thu Mar 30 22:53:52 2006: Request 18460 was acted upon. > Transaction: Ticket created by MSCHWERN > Queue: Class-Autouse > Subject: "use lock" doesn't work... which is good. > Owner: Nobody > Requestors: MSCHWERN@cpan.org > Status: new > Ticket <URL: http://rt.cpan.org/Ticket/Display.html?id=18460 > > > > I noticed this bit of code in Class::Autouse... > > # Add the AUTOLOAD hook and %INC lock to prevent 'use'ing > *{"${class}::AUTOLOAD"} = \&_AUTOLOAD; > $INC{$file} = 'Class::Autouse'; > > which worried me because the code base I'm applying Autouse to has > hybrid modules, they're both classes and export functions. Its a large, > crufty code base and not what I prefer, but I can't change that now. If > the feature above worked it would prevent this sort of thing... > > Class::Autouse->autouse("Some::Thing"); > > use Some::Thing qw(function); > > Fortunately it doesn't work. :) Here's a simple example. > > #!/usr/bin/perl -wl > > use strict; > use Class::Autouse; > Class::Autouse->autouse('Foo'); > use Foo; > > print $INC{"Foo.pm"}; > print Foo::thing(); > > where Foo.pm is > > package Foo; > > sub thing { 42 } > > 1; > > > Autouse's attempt to prevent loading is defeated, I suspect, because > "use" calls import() which triggers the loading of the module. However, > require is defeated. This breaks certain techniques... > > #!/usr/bin/perl -wl > > use strict; > use Class::Autouse; > Class::Autouse->autouse('Foo'); > require Foo; > Foo->import; > > print $INC{"Foo.pm"}; > print thing(); > > Where Foo.pm is > > package Foo; > > use base qw(Exporter); > use vars qw(@EXPORT); > @EXPORT = qw(thing); > > sub thing { 42 } > > 1; > > Comment out the autouse() and it works fine. Put it back and thing() is > undefined. I'm not sure why. If you go in with the debugger you'll > notice Foo->import is going directly into Exporter->import rather than > Autouse's loader even though the real Foo.pm remains unloaded. I don't > know why this is. > > Anyhow, as the "use lock" is not doing its intended job and reduces the > transparency of Autouse in this case, I'd recommend removing it.
On Thu Mar 30 23:23:47 2006, adam@phase-n.com wrote: Show quoted text
> Oops, that is a typo in the comment. > > The purpose is to prevent... > > use Foo; > > ... from actually loading anything, and to return silently.
I thought it didn't work but it appears that it does. My mistake was forgetting to call Class::Autouse->autouse in a BEGIN block while testing it. Show quoted text
> Most of the magic of Class::Autouse, as well as the reason for its > namespace, is that it trades off any support for things that aren't pure > classes.
There is the additional trade off of being unable to force a class to load which causes problems with testing techniques involving overriding methods or resolving circular dependencies. I suppose one can use Class::Autouse->load for that, but it does make it more difficult to simply apply Class::Autouse to an existing wad of code. Show quoted text
> That is, you don't get to use ->import, and in exchange it will make > like SO much more simple. > > Class::Autouse is fundamentally incompatible with anything that needs to > do more than > > BEGIN { > require Module::Name; > }
If the use lock could be optionally removed then Class::Autouse would work transparently on hybrid modules (ie. that are both classes and export) and allow existing code which relies on load order to continue to work without modification. Patch forthcoming.
Subject: Re: [rt.cpan.org #18460] "use lock" doesn't work... which is good.
Date: Sun, 02 Apr 2006 13:12:47 +1000
To: bug-Class-Autouse [...] rt.cpan.org
From: Adam Kennedy <adam [...] phase-n.com>
Show quoted text
> There is the additional trade off of being unable to force a class to > load which causes problems with testing techniques involving overriding > methods or resolving circular dependencies. I suppose one can use > Class::Autouse->load for that, but it does make it more difficult to > simply apply Class::Autouse to an existing wad of code.
hmm... Yes, ->load is there for if you want to explicitly force just one single class to be loaded. Or, in testing and for development stuff, there is the :devel flag, which disables autoloading. So in your test script, the first thing you do, before loading any modules, is... use Class::Autouse ':devel'; And it disables autoloading altogether, loading the modules immediately. Of course, it still won't call your ->import for you. Show quoted text
>>That is, you don't get to use ->import, and in exchange it will make >>like SO much more simple. >> >>Class::Autouse is fundamentally incompatible with anything that needs to >>do more than >> >>BEGIN { >> require Module::Name; >>}
> > > If the use lock could be optionally removed then Class::Autouse would > work transparently on hybrid modules (ie. that are both classes and > export) and allow existing code which relies on load order to continue > to work without modification. > > Patch forthcoming.
I'm very nervous about this. Mainly because I've put in so much effort to prevent people having to know too much about the ugly details of how the module works. While as I said, I do not want to support exports, it currently does not in general prevent you from doing load-order dependant code, providing you follow full encapsulation, and insist that the startup stuff be called as methods, and don't play with variables directly. So while use Module qw{ this that }; won't work, you can certainly do use Module; Module->do_something; ... and it will work properly. Actually, I'm left wondering why C< use Module qw{ this that } > doesn't trigger loading anyway. I suspect it is because Exporter itself isn't fully OO, and looks for variables inside your class directly. So if you REALLY want to have export working, it should be possible to make a Class::Autouse-compatible hybrid class. Instead of modifying C:A to suit your module, you could modify your module to suit C:A. package Hybird; require Exporter; sub import { Exporter::import(@_); } 1; That trick should make your module behave more appropriately. Let me know if it works for you. Adam K
Here's my scenario. I'm applying Class::Autouse to a large, existing, commercial code base. How large is this code base? 400K lines. That's including tests (probably about half that is tests) but not including the Mason code. I've been grudgingly allowed to apply Autouse to the code as long as it will have a minimal impact and take a minimum of time. You'll understand if I want Class::Autouse to be as transparent as possible and do not (cannot even) alter the code to suit Class::Autouse. The code is overwhelmingly OO. It almost always loads OO modules via Aliased.pm (very similar to aliased.pm on CPAN). I've simply replaced the "require $module" in Aliased.pm with "Class::Autouse->autouse($module)". This applies autouse while changing very little code but not giving up all control to the superloader (which I don't mind but it makes the other programmers nervous). On Sat Apr 01 22:13:11 2006, adam@phase-n.com wrote: Show quoted text
> Yes, ->load is there for if you want to explicitly force just one single > class to be loaded.
While this might work for new code, existing code will not be doing this. It also means the user has to be aware that the module is Autouse'd which violates the transparency of autousing. Show quoted text
> Or, in testing and for development stuff, there is the :devel flag, > which disables autoloading.
If that is used the tests are running under different conditions than the live code. That is, I'm not testing that autousing. It might cause a problem possibly through a different load order or perhaps unexpected opacity in Class::Autouse such as the ones I'm encountering. Show quoted text
> Of course, it still won't call your ->import for you.
Let's put import() aside for a moment. I've attached a test which demonstrates the problem with the require/use lock in a pure OO class. I'm working on a patch to make it so the lock can be turned off with a class method.
#!/usr/bin/perl -w use strict; use lib (); use File::Spec::Functions ':ALL'; BEGIN { $| = 1; if ( $ENV{HARNESS_ACTIVE} ) { lib->import( catdir( curdir(), 't', 'modules' ) ); } else { require FindBin; chdir ($FindBin::Bin = $FindBin::Bin); # Avoid a warning lib->import( 'modules' ); } } use Test::More tests => 4; use Class::Autouse; BEGIN { Class::Autouse->autouse('D'); } use D; { local $^W = 0; local *D::method2 = sub { 2 }; is( D->method, 1 ); is( D->method2, 2 ); } is( D->method, 1 ); is( D->method2, 1 );