Skip to content

Can not trap utf8 decode errors with eval{} (or suppress warnings) #304

Description

@jimav

slurp_utf8 decode errors do not throw an exception, but just print warning messages and control returns normally.

This prevents doing e.g.
eval{ path(...)->slurp_utf8 } // handle_decode_error();

The problem is that Unicode::UTF8::decode_utf8 spews warnings if 'utf8' warnings are enabled, and the only way to detect errors programmatically is with use warnings FATAL => 'utf8' and catching the exception. Path::Tiny uses a file-scope use warnings; which overrides attempts to work around the problem in user code.

SUGGESTED SOLUTION:
Make sub slurp_utf8 use a localized use warnings FATAL => 'utf8'; so that an exception will be thrown if appropriate (and nothing will be printed to STDERR).

Here is a test version which seems to work:

sub slurp_utf8 {
# <test addition>
  eval { use warnings FATAL => 'utf8';
# </test addition>
    
    if ( defined($HAS_UU) ? $HAS_UU : ( $HAS_UU = _check_UU() ) ) {
        return Unicode::UTF8::decode_utf8( slurp( $_[0], { binmode => ":unix" } ) );
    }
    elsif ( defined($HAS_PU) ? $HAS_PU : ( $HAS_PU = _check_PU() ) ) {
        $_[1] = { binmode => ":unix:utf8_strict" };
        goto &slurp;
    }   
    else {
        $_[1] = { binmode => ":unix:encoding(UTF-8)" };
        goto &slurp;
    }

# <test addition>
  }
  // do{
    Path::Tiny::Error->throw( "slurp_utf8", $_[0], $@ );
  }
# </test addition>
}

DEMO SCRIPT

#!/usr/bin/env perl
use strict; use warnings; use v5.32;
use open OUT => 'utf8', ':std'; STDOUT->autoflush(1); STDERR->autoflush(1);

#my $octets = "ABC";
my $octets = "\x{80}";

{
  use Unicode::UTF8 qw[decode_utf8 encode_utf8];
  use warnings FATAL => 'utf8'; # fatalize encoding glitches
  my $chars = eval{ decode_utf8($octets) };
  if ($@) {
    warn "Caught decode_utf8 exception:\n   $@\n";
  } else {
    warn "decode_utf8 SUCCEEDED! chars='$chars'\n";
  }
}

{
  use Path::Tiny;
  my $tpath = Path::Tiny::tempfile("octets_XXXX"); $tpath->spew_raw($octets);

  use warnings FATAL => 'utf8'; # makes no difference!
  my $chars = eval{ $tpath->slurp_utf8; };
  if ($@) {
    warn "Caught slurp_utf8 exception: $@\n";
  } else {
    warn "slurp_utf8 SUCCEEDED! chars='$chars'\n";
  }
}

Results using Path::Tiny version 0.146

Caught decode_utf8 exception:
   Can't decode ill-formed UTF-8 octet sequence <80> in position 0 at /tmp/t1 line 11.
 
Can't decode ill-formed UTF-8 octet sequence <80> in position 0 at /home/jima/perl5/lib/perl5/Path/Tiny.pm line 2087.
slurp_utf8 SUCCEEDED!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions