Filter::Handle Issue in 5.8+

leif.eriksen at hpa.com.au leif.eriksen at hpa.com.au
Sun Dec 14 05:09:25 CST 2003


OK, I described this issue last night before the film, but here is a 
more cogent version.

Filter::Handle is a CPAN module that been around for yonks (2years +), 
and works well in 5.005 and 5.6, but seems broken in one aspect in 5.8+

Filter::Handle allows you to apply arbitrary filters to output 
filehandles. You can perform any sorts of transformations on the 
outgoing text: you can prepend it with some data, you can replace all 
instances of one word with another, etc.

Filter::Handle has 3 interfaces
1. OO
2. Functional
3. tie

example usage
<code>
#!/usr/bin/perl -w
use strict; # is gay
use IO::File;
use POSIX;
use Filter::Handle qw/subs/;

my @captured;
my $fh = IO::File->open($fname, O_RDWR|O_CREAT|O_TRUNC) or
    die "a horrible death :: $!\n";

tie $fh, Filter::Handle, sub {
    push @captured, @_;
    ();
};

# ... series of slow and expensive calls that populate @captured
# ... series of quick adjustments to @captured rather than repeat slow calls
untie $fh;

print $fh @captured # big dump
exit 0;
</code>

This code intercepts all writes to $fh, placing them in @captured. When 
we have assembled everything, we write everything to the real file in 
one big and (hopefully) efficient print.

The problem is one of the test cases for Filter::Handle fails under 5.8+

## 3. Test Filter/UnFilter routines.
my $out;
Filter \*STDOUT, sub {
    $out = sprintf "%d: %s\n", 1, "@_";
    ()
};
print "Foo"; # FAILS !!!!
UnFilter \*STDOUT;

The reason for the failure is a deeply recursive call inside 
Filter::Handle -
package Filter::Handle;
<snip>
sub Filter {
    my $fh = $_[0];
    tie *{ $fh }, __PACKAGE__, @_;
}

sub PRINT {
    my $self = shift;
    my $fh = *{ $self->{fh} };
    print $fh $self->{output}->(@_); # FAILS HERE
}
*print = *PRINT;
1;

The line marked 'FAILS HERE' is the problem.

Normally when you tie a variable, it is the variable that is the subject 
of the tie, not the value. So
<code>
$scalar = 'text';
tie $scalar, Module;
</code>
ties $scalar to Module, not 'text'. In the package that $scalar was tied 
to, access's to $scalar's value result in normal function calls to 
Module, which (hopefully) implements the required tie scalar interface. 
Inside Module's namespace, access's operate on the real value.
e.g.
<code>
#!/usr/bin/perl -w
package Module;
sub TIESCALAR {bless \my $inner, shift;}
sub FETCH {
    my $impl = shift;
    print "accessed value $$impl by ", (caller(1))[3], "\n";
    $$impl;
}
sub STORE {
    my $impl = shift;
    print 'set by ', (caller(1))[3], " to value ", $$impl = shift, "\n";
}
sub DESTROY {}
1;
package Run;
sub run {
    tie my $tracked, Module;
    $tracked = 'here';
    print $tracked, "\n";
}
1;
package main;
Run::run();
</code>
<output>
set by Run::run to value here
accessed value here by Run::run
here
</output>
Back to my problem
package Filter::Handle;
sub PRINT {
    my $self = shift;
    my $fh = *{ $self->{fh} };
    print $fh $self->{output}->(@_); # FAILS HERE
}

For my problem, the $fh is not the real file handle but is the tied 
filehandle again, meaning PRINT() is called again (and again, and 
again...). This didn't used to happen in perl < 5.8. Why this happens 
and how to fix it are a mystery to me, but I hesitate to name it as a 
bug in 5.8+, because the people who wrote that are way smarter than I am...

Anybody want to give me a clue...

-- 
Leif Eriksen
Senior Analyst/Programmer

HPA
Direct: +61 3 9217 5545
Fax   : +61 3 9217 5702

http://www.hpa.com.au/




**********************************************************************
IMPORTANT
The contents of this e-mail and its attachments are confidential and intended
solely for the use of the individual or entity to whom they are
addressed.  If you received this e-mail in error, please notify
the HPA Postmaster, postmaster at hpa.com.au, then delete 
the e-mail.

This footnote also confirms that this e-mail message has been swept for
the presence of computer viruses by MimeSweeper.  Before opening or
using any attachments, check them for viruses and defects.

Our liability is limited to resupplying any affected attachments.

HPA collects personal information to provide and market our services.
For more information about use, disclosure and access see our Privacy
Policy at www.hpa.com.au
**********************************************************************




More information about the Melbourne-pm mailing list