Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Re^3: How to redefine a modules private function?

by haukex (Archbishop)
on Mar 08, 2022 at 16:07 UTC ( [id://11141919]=note: print w/replies, xml ) Need Help??


in reply to Re^2: How to redefine a modules private function?
in thread How to redefine a modules private function?

Frankly, i would consider the use of a constant function (or other type of constant) for a port number a major bug in AnyEvent::DNS. There are very few use cases that make sense to declare a number a constant in programming code.

I assume you're not advocating for magic numbers - I might have said that a constant that isn't modifiable from outside the module is a problem.

I tried using source filters (Filter::Simple), but couldn't make it work

This was a fun project. I didn't exactly use source filters, but I did use PPI to munge the module before loading it, basically like a filtered use. Obviously this still has all the limitations of source filters and PPI!

Deconstifier.pm

package Deconstifier; use warnings; use strict; use parent 'PPI::Transform'; =head1 DESCRIPTION L<PPI::Transform> implementation that modifies a subset of L<constant functions|perlsub/"Constant Functions"> such that they are +no longer inlined. The subset of C<sub> definitions that are currently supported + is: sub FOO () { 42; } sub BAR () { "string"; } sub QUZ () { undef; } where the final semicolon is optional. =cut sub document { ( my $self = shift )->isa(__PACKAGE__) or return undef; ( my $doc = shift )->isa('PPI::Document') or return undef; my $subs = $doc->find(sub { if ( $_[1]->isa('PPI::Statement::Sub') && defined($_[1]->proto +type) && $_[1]->prototype eq "" ) { my $bl = $_[1]->block; if ( $bl && $bl->schildren==1 && $bl->schild(0)->isa('PPI: +:Statement') ) { my $st = $bl->schild(0); if ( $st->schildren==1 || $st->schildren==2 && $st->sc +hild(1)->isa('PPI::Token::Structure') && $st->schild(1)->content eq ' +;' ) { my $ch = $st->schild(0); if ( $ch->isa('PPI::Token::Number') || $ch->isa('P +PI::Token::Quote') || $ch->isa('PPI::Token::Word') && $ch->literal eq + 'undef' ) { return 1; } } } } return 0; }); return undef unless defined $subs; return 0 unless $subs; for my $s (@$subs) { #use PPI::Dumper; PPI::Dumper->new($s, whitespace=>0, comments +=>0)->print; # This first one only seems to work on Perl 5.8+, the second d +own to 5.6 and maybe/likely earlier (untested). # NOTE: This isn't really the right way to use PPI::Token::Wor +d, but since it's the only modification we're making it works fine. #$s->block->schild(0)->schild(0)->insert_before(PPI::Token::Wo +rd->new('return ')); $s->block->schild(0)->schild(0)->insert_after(PPI::Token::Word +->new(' if $]')); } return 0+@$subs; } 1;

deconstify.t

use warnings; use strict; use Test::More tests=>4; BEGIN { use_ok 'Deconstifier' } my $code = <<'END'; sub MAX_PKT() { 4096.0 } sub DOMAIN_PORT() { 53; } sub resolver (); sub _enc_qd() { (_enc_name $_->[0]) . pack "nn", ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}), ($_->[3] > 0 ? $_->[2] : $class_id{$_->[2] || "in"}) } sub _enc_rr() { die "encoding of resource records is not supported"; } sub HELLO { "world" } sub WORLD () { "foo" } sub FOO () { $bar } sub BAR () { return 123 } sub BLAH () { undef; } END my $exp = <<'END'; sub MAX_PKT() { 4096.0 if $] } sub DOMAIN_PORT() { 53 if $]; } sub resolver (); sub _enc_qd() { (_enc_name $_->[0]) . pack "nn", ($_->[1] > 0 ? $_->[1] : $type_id {$_->[1]}), ($_->[3] > 0 ? $_->[2] : $class_id{$_->[2] || "in"}) } sub _enc_rr() { die "encoding of resource records is not supported"; } sub HELLO { "world" } sub WORLD () { "foo" if $] } sub FOO () { $bar } sub BAR () { return 123 } sub BLAH () { undef if $]; } END my $trans = new_ok 'Deconstifier'; ok $trans->apply(\$code), 'apply'; is $code, $exp, 'output is as expected';

FilterLoad.pm

package FilterLoad; use warnings; use strict; use List::Util qw/ pairs pairkeys /; use PPI; use Module::Load::Conditional qw/ check_install /; use Module::Runtime qw/ use_module module_notional_filename /; =head1 DESCRIPTION Loads modules after passing them through the L<PPI::Transform> filter( +s) given in the C<use> statement. For example: use FilterLoad 'AnyEvent::DNS' => 'Deconstifier', SomeModule => 'Deconstifier'; =cut sub import { my ($class, @defs) = @_; my (%mods, %filts); for ( pairs @defs ) { my ($mod, $filt) = @$_; $filts{$filt}++; my $modfn = module_notional_filename($mod); $mods{$modfn}{name} = $mod; push @{ $mods{$modfn}{filts} }, $filt; } use_module($_) for keys %filts; our $_in_inc_hook; unshift @INC, sub { my ($self, $modfn) = @_; return if $_in_inc_hook; return unless exists $mods{$modfn}; local $_in_inc_hook = 1; # check_install calls @INC hooks! my $info = check_install(module=>$mods{$modfn}{name}) or die "could not find $modfn"; my $doc = PPI::Document->new($info->{file}); $_->new->apply($doc) for @{ $mods{$modfn}{filts} }; return \$doc->serialize; }; use_module($_) for pairkeys @defs; } 1;

test.pl

use warnings; use strict; use FilterLoad Foo => 'Deconstifier', 'AnyEvent::DNS' => 'Deconstifier'; sub Foo::ONE () { 444 } sub Foo::TWO { 555 } sub Foo::THREE () { 666 } Foo::go(); print "One=", Foo::ONE, ", Two=", Foo::TWO, ", Three=", Foo::THREE, "\ +n"; # I've manually edited the module to include a function # sub foobar { print "DOMAIN_PORT=", DOMAIN_PORT, "\n" } AnyEvent::DNS::foobar(); sub AnyEvent::DNS::DOMAIN_PORT () { 4242 } AnyEvent::DNS::foobar();

Using Foo.pm from my node here, and modifying AnyEvent::DNS as noted in the code above, the output is:

Subroutine Foo::ONE redefined at test.pl line 9. Subroutine Foo::TWO redefined at test.pl line 10. Subroutine Foo::THREE redefined at test.pl line 11. Subroutine AnyEvent::DNS::DOMAIN_PORT redefined at test.pl line 19. One=444, Two=555, Three=666 One=444, Two=555, Three=666 DOMAIN_PORT=4242 DOMAIN_PORT=4242

Replies are listed 'Best First'.
Re^4: How to redefine a modules private function?
by cavac (Parson) on Mar 09, 2022 at 14:17 UTC

    I assume you're not advocating for magic numbers - I might have said that a constant that isn't modifiable from outside the module is a problem.

    I agree. I meant "don't use constants, make it variables that can be changed from the outside".

    That said, i'm a lazy bum and i use some magic numbers in code i write.

    • I almost always use the magic numbers 1 and 0 for true and false. I'm just assuming that anyone reading perl code knows what that means.
    • When writing web stuff, i use just the HTTP status codes instead of their names. So i'd write 404 instead of instead of something like $HTTP_STATUS_CODES{'Not Found'}. Force of habit, i guess, because in my brain they're exactly the same thing.
    • When doing basic computer maths like bitshifting, i'm just assuming anyone reading the code understands that $x >> 8 means "shift by one byte to the right".
    • Same goes for $x & 0xff meaning "give me the last 8 bits"

    In my personal opinion, the worst offender in the whole Perl ecosystem when it comes to "magic numbers" is the default variable $_. It has no fixed value, and the perl interpreter pops it silently into your code whenever you forget to specify a variable.

    perl -e 'use Crypt::Digest::SHA256 qw[sha256_hex]; print substr(sha256_hex("the Answer To Life, The Universe And Everything"), 6, 2), "\n";'

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11141919]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others browsing the Monastery: (8)
As of 2024-04-19 09:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found