r107016 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107015‎ | r107016 | r107017 >
Date:00:00, 22 December 2011
Author:grafzahl
Status:deferred (Comments)
Tags:score 
Comment:
Improved error reporting:

* Altered ScoreException to accept error message parameters
* Use error CSS for ScoreCallException
* The error messages themselves have been made clearer
Modified paths:
  • /trunk/extensions/Score/Score.body.php (modified) (history)
  • /trunk/extensions/Score/Score.i18n.php (modified) (history)

Diff [purge]

Index: trunk/extensions/Score/Score.i18n.php
@@ -27,50 +27,53 @@
2828
2929 /* English */
3030 $messages['en'] = array(
31 - 'score-abc2lynotexecutable' => 'ABC to LilyPond converter could not be executed.',
 31+ 'score-abc2lynotexecutable' => 'ABC to LilyPond converter could not be executed: $1 is not an executable file. Make sure <code>$wgAbc2Ly</code> is set correctly.',
3232 'score-abcconversionerr' => 'Unable to convert ABC file to LilyPond format:
3333 $1',
34 - 'score-noabcinput' => 'ABC source file could not be created',
35 - 'score-chdirerr' => 'Unable to change directory',
 34+ 'score-chdirerr' => 'Unable to change to directory $1',
3635 'score-cleanerr' => 'Unable to clean out old files before re-rendering',
3736 'score-compilererr' => 'Unable to compile LilyPond input file:
3837 $1',
3938 'score-desc' => 'Adds a tag for rendering musical scores with LilyPond',
4039 'score-getcwderr' => 'Unable to obtain current working directory',
41 - 'score-invalidlang' => 'Invalid score language specified. Currently recognised languages are lang="lilypond" (the default) and lang="ABC".',
42 - 'score-nooutput' => 'Failed to create LilyPond image directory',
 40+ 'score-invalidlang' => 'Invalid score language lang="$1". Currently recognised languages are lang="lilypond" (the default) and lang="ABC".',
 41+ 'score-noabcinput' => 'ABC source file $1 could not be created.',
 42+ 'score-nooutput' => 'Failed to create LilyPond image directory $1',
4343 'score-nofactory' => 'Failed to create LilyPond factory directory',
44 - 'score-noinput' => 'Failed to create LilyPond input file',
45 - 'score-notexecutable' => 'Could not execute LilyPond. Make sure <code>$wgLilyPond</code> is set correctly.',
 44+ 'score-noinput' => 'Failed to create LilyPond input file $1.',
 45+ 'score-notexecutable' => 'Could not execute LilyPond: $1 is not an executable file. Make sure <code>$wgLilyPond</code> is set correctly.',
4646 'score-page' => 'Page $1',
4747 'score-pregreplaceerr' => 'PCRE regular expression replacement failed',
48 - 'score-readerr' => 'Unable to read file',
 48+ 'score-readerr' => 'Unable to read file $1',
4949 'score-renameerr' => 'Error moving score files to upload directory',
50 - 'score-trimerr' => 'Image could not be trimmed. Set $wgScoreTrim=false if this problem persists.',
51 - 'score-versionerr' => 'Unable to obtain LilyPond version.',
 50+ 'score-trimerr' => 'Image could not be trimmed:
 51+$1
 52+Set <code>$wgScoreTrim=false</code> if this problem persists.',
 53+ 'score-versionerr' => 'Unable to obtain LilyPond version:
 54+$1',
5255 );
5356
5457 /** Message documentation (Message documentation) */
5558 $messages['qqq'] = array(
56 - 'score-abc2lynotexecutable' => 'Displayed if the ABC to LilyPond converter could not be executed.',
 59+ 'score-abc2lynotexecutable' => 'Displayed if the ABC to LilyPond converter could not be executed. $1 is the path to the abc2ly binary.',
5760 'score-abcconversionerr' => 'Displayed if the ABC to LilyPond conversion failed. $1 is the error (generally big block of text in a pre tag)',
58 - 'score-noabcinput' => 'Displayed if an ABC source file could not be created for lang="ABC".',
59 - 'score-chdirerr' => 'Displayed if the extension cannot change its working directory.',
 61+ 'score-chdirerr' => 'Displayed if the extension cannot change its working directory. $1 is the path to the target directory.',
6062 'score-cleanerr' => 'Displayed if an old file cleanup operation fails.',
6163 'score-compilererr' => 'Displayed if the LilyPond code could not be compiled. $1 is the error (generally big block of text in a pre tag)',
6264 'score-desc' => '{{desc}}',
63 - 'score-getcwderr' => 'Displayed if the extension cannot obtain the CWD.',
64 - 'score-invalidlang' => 'Displayed if the lang="…" attribute contains an unrecognised score language.',
65 - 'score-nooutput' => 'Displayed if the LilyPond image/midi dir cannot be created.',
 65+ 'score-getcwderr' => 'Displayed if the extension cannot obtain the current working directory.',
 66+ 'score-invalidlang' => 'Displayed if the lang="…" attribute contains an unrecognised score language. $1 is the unrecognised language.',
 67+ 'score-noabcinput' => 'Displayed if an ABC source file could not be created for lang="ABC". $1 is the path to the file that could not be created.',
 68+ 'score-nooutput' => 'Displayed if the LilyPond image/midi dir cannot be created. $1 is the name of the directory.',
6669 'score-nofactory' => 'Displayed if the LilyPond/ImageMagick working directory cannot be created.',
67 - 'score-noinput' => 'Displayed if the LilyPond input file cannot be created.',
68 - 'score-notexecutable' => "Displayed if LilyPond binary can't be executed.",
 70+ 'score-noinput' => 'Displayed if the LilyPond input file cannot be created. $1 is the path to the input file.',
 71+ 'score-notexecutable' => 'Displayed if LilyPond binary cannot be executed. $1 is the path to the LilyPond binary.',
6972 'score-page' => 'The word "Page" as used in pagination. $1 is the page number',
7073 'score-pregreplaceerr' => 'Displayed if a PCRE regular expression replacement failed.',
71 - 'score-readerr' => 'Displayed if the extension could not read a file',
 74+ 'score-readerr' => 'Displayed if the extension could not read a file. $1 is the path to the file that could not be read.',
7275 'score-renameerr' => 'Displayed if moving the resultant files from the working environment to the upload directory fails.',
73 - 'score-trimerr' => 'Displayed if the extension failed to trim an output image.',
74 - 'score-versionerr' => 'Displayed if the extension failed to obtain the version string of LilyPond.',
 76+ 'score-trimerr' => 'Displayed if the extension failed to trim an output image. $1 is the error (generally big block of text in a pre tag)',
 77+ 'score-versionerr' => 'Displayed if the extension failed to obtain the version string of LilyPond. $1 is the LilyPond stdout output generated by the attempt.',
7578 );
7679
7780 /** Danish (Dansk)
Index: trunk/extensions/Score/Score.body.php
@@ -32,10 +32,16 @@
3333 */
3434 class ScoreException extends Exception {
3535 /**
 36+ * Error message parameters
 37+ */
 38+ private $parameters;
 39+
 40+ /**
3641 * Empty constructor.
3742 */
38 - public function __construct( $message, $code = 0, Exception $previous = null ) {
39 - parent::__construct( $message, $code, $previous );
 43+ public function __construct( $message ) {
 44+ $this->parameters = array_slice( func_get_args(), 1 );
 45+ parent::__construct( $message );
4046 }
4147
4248 /**
@@ -48,7 +54,13 @@
4955 return Html::rawElement(
5056 'span',
5157 array( 'class' => 'error' ),
52 - wfMessage( $this->getMessage() )->inContentLanguage()->parse()
 58+ call_user_func_array( array(
 59+ wfMessage( $this->getMessage() )
 60+ ->inContentLanguage(),
 61+ 'params'
 62+ ),
 63+ $this->parameters )
 64+ ->parse()
5365 );
5466 }
5567 }
@@ -71,9 +83,9 @@
7284 * parameter, the error message returned from the binary.
7385 * @param $callErrMsg Raw error message returned by the binary.
7486 */
75 - public function __construct( $message, $callErrMsg, $code = 0, Exception $previous = null ) {
 87+ public function __construct( $message, $callErrMsg ) {
7688 $this->callErrMsg = $callErrMsg;
77 - parent::__construct( $message, $code, $previous );
 89+ parent::__construct( $message );
7890 }
7991
8092 /**
@@ -83,10 +95,14 @@
8496 * @return error message HTML.
8597 */
8698 public function __toString() {
87 - return wfMessage( $this->getMessage() )
88 - ->inContentLanguage()
89 - ->rawParams( Html::rawElement( 'pre', array(), strip_tags( $this->callErrMsg ) ) )
90 - ->parse();
 99+ return Html::rawElement(
 100+ 'span',
 101+ array( 'class' => 'error' ),
 102+ wfMessage( $this->getMessage() )
 103+ ->inContentLanguage()
 104+ ->rawParams( Html::rawElement( 'pre', array(), strip_tags( $this->callErrMsg ) ) )
 105+ ->parse()
 106+ );
91107 }
92108 }
93109
@@ -110,19 +126,19 @@
111127 global $wgLilyPond;
112128
113129 if ( !is_executable( $wgLilyPond ) ) {
114 - throw new ScoreException( 'score-notexecutable' );
 130+ throw new ScoreException( 'score-notexecutable', $wgLilyPond );
115131 }
116132
117 - $cmd = wfEscapeShellArg( $wgLilyPond ) . ' --version';
 133+ $cmd = wfEscapeShellArg( $wgLilyPond ) . ' --version 2>&1'; // FIXME: 2>&1 is not portable
118134 $stdout = wfShellExec( $cmd, $rc );
119135 if ( $rc != 0 ) {
120 - throw new ScoreException( 'score-versionerr' );
 136+ throw new ScoreCallException( 'score-versionerr', $stdout );
121137 }
122138
123139 $n = sscanf( $stdout, 'GNU LilyPond %s', self::$lilypondVersion );
124140 if ( $n != 1 ) {
125141 self::$lilypondVersion = null;
126 - throw new ScoreException( 'score-versionerr' );
 142+ throw new ScoreCallException( 'score-versionerr', $stdout );
127143 }
128144 }
129145
@@ -180,7 +196,7 @@
181197 }
182198 $rc = file_put_contents( $lilypondFile, $lilypondCode );
183199 if ( $rc === false ) {
184 - throw new ScoreException( 'score-noinput' );
 200+ throw new ScoreException( 'score-noinput', $lilypondFile );
185201 }
186202 break;
187203 case 'ABC':
@@ -188,7 +204,7 @@
189205 self::runAbc2Ly( $code, $factoryDirectory );
190206 break;
191207 default:
192 - throw new ScoreException( 'score-invalidlang' );
 208+ throw new ScoreException( 'score-invalidlang', $lang );
193209 }
194210
195211 $html = self::runLilypond( $factoryDirectory, $renderMidi, $altText );
@@ -258,12 +274,12 @@
259275 /* Create ABC input file */
260276 $rc = file_put_contents( $abcFile, ltrim( $code ) ); // abc2ly is picky about whitespace at the start of the file
261277 if ( $rc === false ) {
262 - throw new ScoreException( 'score-noabcinput' );
 278+ throw new ScoreException( 'score-noabcinput', $abcFile );
263279 }
264280
265281 /* Convert to LilyPond file */
266282 if ( !is_executable( $wgAbc2Ly ) ) {
267 - throw new ScoreException( 'score-abc2lynotexecutable' );
 283+ throw new ScoreException( 'score-abc2lynotexecutable', $wgAbc2Ly );
268284 }
269285
270286 $cmd = wfEscapeShellArg( $wgAbc2Ly )
@@ -283,7 +299,7 @@
284300 /* The output file has a tagline which should be removed in a wiki context */
285301 $lyData = file_get_contents( $lyFile );
286302 if ( $lyData === false ) {
287 - throw new ScoreException( 'score-readerr' );
 303+ throw new ScoreException( 'score-readerr', $lyFile );
288304 }
289305 $lyData = preg_replace( '/^(\s*tagline\s*=).*/m', '$1 ##f', $lyData );
290306 if ( $lyData === null ) {
@@ -291,7 +307,7 @@
292308 }
293309 $rc = file_put_contents( $lyFile, $lyData );
294310 if ( $rc === false ) {
295 - throw new ScoreException( 'score-noinput' );
 311+ throw new ScoreException( 'score-noinput', $lyFile );
296312 }
297313 }
298314
@@ -322,7 +338,7 @@
323339 $lilypondDir = 'lilypond';
324340 $md5 = md5_file( $lilypondFile );
325341 if ( $md5 === false ) {
326 - throw new ScoreException( 'score-noinput' );
 342+ throw new ScoreException( 'score-noinput', $lilypondFile );
327343 }
328344 $rel = $lilypondDir . '/' . $md5; // FIXME: Too many files in one directory?
329345 $filePrefix = "$wgUploadDirectory/$rel";
@@ -370,7 +386,7 @@
371387 if ( !file_exists( "$wgUploadDirectory/$lilypondDir" ) ) {
372388 $rc = wfMkdirParents( "$wgUploadDirectory/$lilypondDir", null, __METHOD__ );
373389 if ( !$rc ) {
374 - throw new ScoreException( 'score-nooutput' );
 390+ throw new ScoreException( 'score-nooutput', $lilypondDir );
375391 }
376392 }
377393
@@ -381,10 +397,10 @@
382398 }
383399 $rc = chdir( $factoryDirectory );
384400 if ( !$rc ) {
385 - throw new ScoreException( 'score-chdirerr' );
 401+ throw new ScoreException( 'score-chdirerr', $factoryDirectory );
386402 }
387403 if ( !is_executable( $wgLilyPond ) ) {
388 - throw new ScoreException( 'score-notexecutable' );
 404+ throw new ScoreException( 'score-notexecutable', $wgLilyPond );
389405 }
390406 $cmd = wfEscapeShellArg( $wgLilyPond )
391407 . " -dsafe='#t' -dbackend=eps --png --header=texidoc "
@@ -393,7 +409,7 @@
394410 $output = wfShellExec( $cmd, $rc2 );
395411 $rc = chdir( $oldcwd );
396412 if ( !$rc ) {
397 - throw new ScoreException( 'score-chdir' );
 413+ throw new ScoreException( 'score-chdirerr', $oldcwd );
398414 }
399415 if ( $rc2 != 0 ) {
400416 throw new ScoreCallException( 'score-compilererr', $output );
@@ -486,10 +502,11 @@
487503 $cmd = wfEscapeShellArg( $wgImageMagickConvertCommand )
488504 . ' -trim '
489505 . wfEscapeShellArg( $source ) . ' '
490 - . wfEscapeShellArg( $dest );
491 - wfShellExec( $cmd, $rc );
 506+ . wfEscapeShellArg( $dest )
 507+ . ' 2>&1'; // FIXME: not portable
 508+ $output = wfShellExec( $cmd, $rc );
492509 if ( $rc != 0 ) {
493 - throw new ScoreException( 'score-trimerr' );
 510+ throw new ScoreCallException( 'score-trimerr', $output );
494511 }
495512 }
496513

Comments

#Comment by Bawolff (talk | contribs)   00:20, 22 December 2011
-			wfMessage( $this->getMessage() )->inContentLanguage()->parse()
+			call_user_func_array( array(
+					wfMessage( $this->getMessage() )
+						->inContentLanguage(),
+					'params'
+				),
+				$this->parameters )
+				->parse()

call_user_func_array is unnessary. wfMessage( 'foo' )->inContentLanguages()->params( array( '1st param', '2nd param', '3rd param' ) ); is just as valid as doing wfMessage( 'foo' )->inContentLanguages()->params( '1st param', '2nd param', '3rd param' );


btw, you don't necessarily have to make messages for every possible error that could occour. If its the type of error that should _never_ happen in practise, and nothing the user could do could possibly trigger it, its also ok to just throw an uncaught exception (provided the exception extends MWException) which would show an internal error notice. This would be ok for something like failed to change directory (imo). On the other hand, its also perfectly ok to do error messages in the fashion you're currently doing them.

#Comment by GrafZahl (talk | contribs)   00:36, 22 December 2011

Oh, OK, thank you. I'll fix it in my next commit.

As for the improbable exceptions, then I guess I have less work in the future ;)

#Comment by Nikerabbit (talk | contribs)   08:56, 22 December 2011

Would it be easier to just pass the Message objects around? Then the caller can feed the parameters to that message and the exception can format it how it wants.

#Comment by GrafZahl (talk | contribs)   11:46, 22 December 2011

Sure, but wouldn't that mean more work for the caller? Like

throw ScoreException( wfMessage( 'score-foo' )->inContentLanguage()->params( $bar1, $bar2, $bar3 ) );

every time an exception is thrown?

#Comment by Nikerabbit (talk | contribs)   12:15, 22 December 2011

Make it either one of these:

#1 throw ScoreException( wfMessage( 'score-foo' )->params( $bar1, $bar2, $bar3 ) );
#2 throw ScoreException( wfMessage( 'score-foo, $bar1, $bar2, $bar3 ) );

You can do the ->inContentLanguage() inside the exception. Then this isn't any more work for the caller compared to providing the message name and params separated.

#Comment by GrafZahl (talk | contribs)   17:08, 22 December 2011

OK, that's convenient. No need to duplicate wfmessage() in the exception then. Thank you!

Status & tagging log