IO::Die - Namespaced, error-checked I/O
Version 0.057
use IO::Die; #Will throw on error: IO::Die->open( my $fh, '<', '/path/to/file' ); IO::Die->print( $fh, 'Some output...' ); IO::Die->close( $fh ); #---------------------------------------------- #...or, perhaps more usefully: package MyIO; use parent 'IO::Die'; sub _CREATE_ERROR { my ( $NS, $type, %args ) = @_; return MyErrorClass->new( $type, %args ); } sub _DO_WITH_ERROR { my ( $NS, $err ) = @_; #$err is the result of _CREATE_ERROR() above return warn $err; } MyIO->open( .. ); #will warn() a MyErrorClass object on error #---------------------------------------------- # You can also do: package MyDynamicIO; use parent 'IO::Die' qw(:preload); #load everything at once sub new { #...something that sets an internal coderef “_create_err_cr” } sub _CREATE_ERROR { my ( $self, $type, %args ) = @_; return $self->{'_create_err_cr'}->($type, %args); } MyDynamicIO->sysopen( .. ); #uses “_create_err_cr” above
This module wraps most of Perl’s built-in I/O functions with code that throws exceptions if the requested operation fails. It confers many of autodie’s benefits but with some distinctions from that module that you may appreciate:
autodie
* IO::Die does not overwrite Perl built-ins.
IO::Die
* IO::Die does not clobber globals like $! and $^E.
* IO::Die does not use function prototypes and does not export: all calls into IO::Die (or subclasses) “look like” what they are.
* IO::Die does not try to impose its own error handling; you can customize both how to represent errors and what to do with them.
* IO::Die seems lighter than autodie in simple memory usage checks. YMMV.
For the most part, you can simply replace:
read( ... );
...with:
IO::Die->read( ... );
This module, though, explicitly rejects certain “unsafe” practices that Perl built-ins still support. Neither bareword file handles nor one-/two-arg open(), for example, are supported here--partly because it’s more complicated to implement, but also because those patterns seem best avoided anyway. Damian Conway’s “Perl Best Practices” and the present author’s experience largely inform discernment of the above, which is admittedly subjective by nature.
open()
This module also rejects use of a single Perl command to operate on multiple files, as e.g. Perl’s chmod() allows. This is because that is the only way to have reliable error checking, which is the whole point of this module.
chmod()
Finally, since this doesn’t use function prototypes, some of the syntaxes that Perl’s built-ins support won’t work here. You’ll likely find yourself needing more parentheses here.
The intent, though, is that no actual functionality of Perl’s built-ins is unimplemented; you may just need to rewrite your calls a bit to have this module perform a given operation. For example:
open( GLOBALS_R_BAD, '>somefile' ); IO::Die->open( my $good_fh, '>', 'somefile'); chown( $uid, $gid, qw( file1 file2 file 3 ) ); IO::Die->chown( $uid, $gid, $_ ) for ( qw( file1 file2 file3 ) ); print { $wfh } 'Haha'; IO::Die->print( $wfh, 'Haha' );
(And, yes, unlike autodie, IO::Die has a print() function!)
print()
Most Perl built-ins that autodie overrides have corresponding functions in this module. Some functions, however, are not implemented here by design:
* readline() and readdir(): Perl’s built-ins do lots of "magic" (e.g., considering '0' as true in a while()) that would be hard to implement.
readline()
readdir()
while()
* system(): This one does a lot “under the hood”, and it’s not feasible to avoid clobbering global $? if you use it.
system()
* tell() doesn’t write to $! and so can’t be error-checked.
tell()
* printf() seems not to have much advantage over combining print() and sprintf(). (?)
printf()
sprintf()
Some functions are thus far not implemented, including write(), ioctl(), syscall(), semaphore functions, etc. These can be implemented as needed.
write()
ioctl()
syscall()
As of version 0.057, this module only loads in a stub at first; each I/O method that you call from this module is then lazy-loaded. This is meant to make this module use the least amount of memory possible.
If you want to load everything all at once (as 0.056 and prior versions did), pass the :preload tag when you use() this module, e.g.:
:preload
use()
use IO::Die qw(:preload);
The following are not complete descriptions of each function; rather, this describes the differences between the relevant Perl built-in and IO::Die’s wrapper of it. It is assumed that the reader is familiar with the built-in form.
Note that NONE of the following functions support bareword filehandles.
This supports all built-in forms of 3 or more arguments. It ONLY supports the two-argument form when the second argument (i.e., the MODE) is “|-” or “-|”.
Only the four-argument form is permitted.
Unlike Perl’s built-ins, these will only operate on one filesystem node at a time. This restriction is necessary for reliable error reporting because Perl’s built-ins have no way of telling us which of multiple filesystem nodes produced the error.
This always treats the first argument as the program name, so if you do:
IO::Die->exec('/bin/echo haha');
… that will actually attempt to execute a program named echo haha in the directory /bin, which probably isn’t what you wanted and will thus fail. (In the above case, what was likely desired was:
echo haha
/bin
IO::Die->exec('/bin/echo', 'haha');
This function returns the unbuffered file pointer position.
The remaining functions intend to match their corresponding Perl built-ins; differences should be regarded as bugs to be fixed!
IO::Die’s default error format is a rather primitive one that just consists of the error parameters in a string. By default, this error is thrown via Perl’s die() built-in. If you need more robust/flexible error handling, subclass this module, and override the _CREATE_ERROR() and/or _DO_WITH_ERROR methods.
die()
_CREATE_ERROR()
_DO_WITH_ERROR
_DO_WITH_ERROR() receives these parameters:
_DO_WITH_ERROR()
* The namespace
* The error (i.e., the error as returned by _CREATE_ERROR())
_CREATE_ERROR() receives these parameters:
* The namespace.
* A name for the error type, e.g., “FileOpen”.
* A list of key/value pairs that describe the error.
The error types are proprietary to this module and listed below.
Each error type always has the following attributes, which are the same values as their corresponding variables as described in “perldoc perlvar”.
* OS_ERROR
* EXTENDED_OS_ERROR
Additional attributes for each type are listed below.
Felipe Gasper, working for cPanel, Inc.
https://github.com/FGasper/io-die
Open an issue at the GitHub URL above. Patches are welcome!
More tests.
Reduce testing dependencies.
Right now this kind of works on Windows, but the tests use fork(), so there all kinds of weird failures that, while they can happen in real code, don’t really stem from this module.
You can find documentation for this module with the perldoc command.
perldoc IO::Die
Copyright 2015 Felipe Gasper.
This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:
http://www.perlfoundation.org/artistic_license_2_0
Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
To install IO::Die, copy and paste the appropriate command in to your terminal.
cpanm
cpanm IO::Die
CPAN shell
perl -MCPAN -e shell install IO::Die
For more information on module installation, please visit the detailed CPAN module installation guide.