Edgewall Software

Ticket #281 (new defect)

Opened 3 years ago

Last modified 22 months ago

advanced-18n: improve error reporting in case of extraction errors

Reported by: cboos Owned by: palgarvio
Priority: major Milestone: 0.7
Component: Internationalization Version: 0.5.1
Keywords: advanced-i18n Cc:

Description

While adding an i18n:msg attribute in Trac, I've not given enough "parameters" to this attribute when expressions were used in the corresponding element. This has lead to a traceback, which is not very nice, as I first thought I've hit a bug in Genshi.

After looking at the code, it appeared that it was simply an error report. This is not OK, extractors should be able to report errors more conveniently (well, I guess that's a Babel issue actually).

Nevertheless here a two suggested improvements:

Attachments

refactor-error-reporting-for-extraction-errors-r967.diff Download (8.7 KB) - added by cboos 3 years ago.
move all the raise IndexError statements in one place
warn-for-missing-parameters.patch Download (1.9 KB) - added by cboos 3 years ago.
don't raise an IndexError but simply warn about sub-optimal use of the directive
improve_param_error_handling.patch Download (21.4 KB) - added by palgarvio 3 years ago.

Change History

Changed 3 years ago by cboos

move all the raise IndexError statements in one place

Changed 3 years ago by cboos

don't raise an IndexError but simply warn about sub-optimal use of the directive

follow-up: ↓ 2   Changed 3 years ago by palgarvio

Included attachment:refactor-error-reporting-for-extraction-errors-r967.diff Download in [968] Thank You! Nicely done.

Regarding the second patch, I only forced an error to impose good usage of the directive. Either you use it badly and don't use params or you use params and but you provide all of them. Still, logging the error should be cleaner, yes.

I'll see what can be done regarding the logging part of distutils and the stdlib logging.

in reply to: ↑ 1 ; follow-up: ↓ 3   Changed 3 years ago by cboos

Replying to palgarvio:

Either you use it badly and don't use params or you use params and but you provide all of them.

In my case, I used i18n:msg="" (is that what you meant with "you use it badly and don't use params"?) but nevertheless I got an error during extraction. More specifically, it was on this change:

  • trac/versioncontrol/templates/browser.html

    a b  
    108109 
    109110      <table py:if="properties or file" id="info" summary="Revision info"> 
    110111        <tr py:if="file"> 
    111           <th scope="col"> 
     112          <th scope="col" i18n:msg=""> 
    112113            Revision <a href="${href.changeset(rev)}">$rev</a>, ${sizeinfo(file.size)} 
    113114            (checked in by ${authorinfo(file.changeset.author)}, ${dateinfo(file.changeset.date)} ago) 
    114115          </th> 

If this "either/or" behavior is desired, then we could simply do:

  • genshi/filters/i18n.py

    diff -r 6a7faccec71f genshi/filters/i18n.py
    a b  
    947947        elif kind is EXPR: 
    948948            if self.params: 
    949949                param = self.params.pop(0) 
    950             else: 
     950            elif self.orig_params: 
    951951                raise IndexError("'%s' parameters given to 'i18n:%s' but more " 
    952952                                 "expressions used in '%s', line %s" % ( 
    953953                                 ', '.join(self.orig_params), 
    954954                                 self.directive.tagname, 
    955955                                 os.path.basename(pos[0]), pos[1])) 
     956            else: 
     957                param = 'expr%d' % len(self.values) 
    956958            self.string.append('%%(%s)s' % param) 
    957959            self.events.setdefault(self.stack[-1], []).append(None) 
    958960            self.values[param] = (kind, data, pos) 

But ideally still without a traceback ;-)

in reply to: ↑ 2   Changed 3 years ago by palgarvio

Replying to cboos:

Replying to palgarvio:

Either you use it badly and don't use params or you use params and but you provide all of them.

In my case, I used i18n:msg="" (is that what you meant with "you use it badly and don't use params"?) but nevertheless I got an error during extraction. More specifically, it was on this change: {{{ #!diff --- a/trac/versioncontrol/templates/browser.html Mon Dec 08 10:26:41 2008 +0100 +++ b/trac/versioncontrol/templates/browser.html Mon Dec 08 13:32:44 2008 +0100 @@ -108,7 +109,7 @@ <table py:if="properties or file" id="info" summary="Revision info"> <tr py:if="file"> - <th scope="col"> + <th scope="col" i18n:msg=""> Revision <a href="${href.changeset(rev)}">$rev</a>, ${sizeinfo(file.size)} (checked in by ${authorinfo(file.changeset.author)}, ${dateinfo(file.changeset.date)} ago) </th> }}} If this "either/or" behavior is desired, then we could simply do: {{{ #!diff diff -r 6a7faccec71f genshi/filters/i18n.py --- a/genshi/filters/i18n.py Mon Dec 08 10:48:07 2008 +0100 +++ b/genshi/filters/i18n.py Mon Dec 08 13:31:00 2008 +0100 @@ -947,12 +947,14 @@ elif kind is EXPR: if self.params: param = self.params.pop(0) - else: + elif self.orig_params: raise IndexError?("'%s' parameters given to 'i18n:%s' but more " "expressions used in '%s', line %s" % ( ', '.join(self.orig_params), self.directive.tagname, os.path.basename(pos[0]), pos[1])) + else: + param = 'expr%d' % len(self.values) self.string.append('%%(%s)s' % param) self.events.setdefault(self.stack[-1], []).append(None) self.values[param] = (kind, data, pos) }}} But ideally still without a traceback ;-)

Hmm, I guess we imposed good usage from the start :) And yes, a traceback is not a good choice :\

K, I'll have a deeper/cleaner look at it.

  Changed 3 years ago by palgarvio

At first thought I think we need to impose good usage because of translations that change the order or words. So, your proposition although good; we get named parameters and only fail if we have the wrong number of them; won't be that helpful for translators. Having expr0, expr1 ain't that helpful for translators since they probably will have to look them up on the source code and most translators are simple users, not coders, so the source code they're seeing might not help at all. Still I'm with you, let's get rid of that traceback :)

  Changed 3 years ago by palgarvio

What would you like to have, exit on this kind of error letting user known what happened or, just log and skip that extraction? I think stopping might be the way:

  • genshi/filters/i18n.py

    ===================================================================           
     
    1818:note: Directives support added since version 0.6 
    1919""" 
    2020 
     21from distutils import log 
    2122from compiler import ast 
    2223from gettext import NullTranslations 
    2324import os 
     
    947948            if self.params: 
    948949                param = self.params.pop(0) 
    949950            else: 
     951                import sys 
    950952                params = ', '.join(['"%s"' % p for p in self.orig_params if p]) 
    951953                if params: 
    952954                    params = "(%s)" % params 
    953                 raise IndexError("%d parameters%s given to 'i18n:%s' but " 
    954                                  "%d or more expressions used in '%s', line %s" 
    955                                  % (len(self.orig_params), params, 
    956                                     self.directive.tagname, 
    957                                     len(self.orig_params)+1, 
    958                                     os.path.basename(pos[0] or 
    959                                                      'In Memmory Template'), 
    960                                     pos[1])) 
     955                log.error("%d parameters%s given to 'i18n:%s' but " 
     956                          "%d or more expressions used in '%s', line %s" % ( 
     957                          len(self.orig_params), params, self.directive.tagname, 
     958                          len(self.orig_params)+1, 
     959                          os.path.basename(pos[0] or 'In Memmory Template'), 
     960                          pos[1])) 
     961                sys.exit(1) 

Or, if we don't want to rely on distutils.log(I see the above log message, don't know why you had to use stdlib's logging module):

  • genshi/filters/i18n.py

     
    947947            if self.params: 
    948948                param = self.params.pop(0) 
    949949            else: 
     950                import sys 
    950951                params = ', '.join(['"%s"' % p for p in self.orig_params if p]) 
    951952                if params: 
    952953                    params = "(%s)" % params 
    953                 raise IndexError("%d parameters%s given to 'i18n:%s' but " 
    954                                  "%d or more expressions used in '%s', line %s" 
    955                                  % (len(self.orig_params), params, 
    956                                     self.directive.tagname, 
    957                                     len(self.orig_params)+1, 
    958                                     os.path.basename(pos[0] or 
    959                                                      'In Memmory Template'), 
    960                                     pos[1])) 
     954                sys.exit("%d parameters%s given to 'i18n:%s' but " 
     955                         "%d or more expressions used in '%s', line %s. " 
     956                         "Stopped processing." % ( 
     957                         len(self.orig_params), params, self.directive.tagname, 
     958                         len(self.orig_params)+1, 
     959                         os.path.basename(pos[0] or 'In Memmory Template'), 
     960                         pos[1])) 
    961961            self.string.append('%%(%s)s' % param) 
    962962            self.events.setdefault(self.stack[-1], []).append(None) 
    963963            self.values[param] = (kind, data, pos) 

Which one would you prefer?

follow-up: ↓ 7   Changed 3 years ago by cboos

Neither ;-) I don't think it's up to an extract filter to decide to sys.exit() the program from which it is used. E.g. imagine some kind of web application doing message extraction of uploaded source files (or newly committed files).

I'm OK with the idea of stopping the extraction and presenting this as a kind of fatal error, but I guess we'd need to do something at the Babel level in order to implement this cleanly.

On a related note, using distutils.log works, but I considered using logging.getLogger('babel') because Babel itself set it up in one case: when extraction is performed by the standalone pybabel script (see  babel/messages/frontend.py). I wonder if that couldn't also done when extraction is done by the setuptools. Also, during extraction itself, some warnings are directly sent to the stderr, see here  babel/messages/extract.py. So again, this suggests some possible enhancements at the Babel level.

in reply to: ↑ 6   Changed 3 years ago by palgarvio

Replying to cboos:

Neither ;-) I don't think it's up to an extract filter to decide to sys.exit() the program from which it is used. E.g. imagine some kind of web application doing message extraction of uploaded source files (or newly committed files).

Yep, you're right.

I'm OK with the idea of stopping the extraction and presenting this as a kind of fatal error, but I guess we'd need to do something at the Babel level in order to implement this cleanly.

Well, we could just raise the IndexError's and have babel catch them and log them, extractions would continue but Babel would then show all the extraction errors.

On a related note, using distutils.log works, but I considered using logging.getLogger('babel') because Babel itself set it up in one case: when extraction is performed by the standalone pybabel script (see  babel/messages/frontend.py). I wonder if that couldn't also done when extraction is done by the setuptools. Also, during extraction itself, some warnings are directly sent to the stderr, see here  babel/messages/extract.py. So again, this suggests some possible enhancements at the Babel level.

Dunno why we chose to print to sys.stderr on Babel, there should be a reason, can't remember which :)

Distutils is slated to use python's stdlib logging module but that's only for python 3.0. Maybe we can know who is running the code and invoke the proper logger because we should handle both. Again, if we just raise the exception and have Babel catch it, Babel would know where to log it to.

  Changed 3 years ago by palgarvio

The above patch along with  this babel ticket resolves this issue. As a default Babel set's the error callback to which ever logging facility is being used, distutils or logging but users can provide their own to stop the whole process ignore it or do whatever they want.

This patch depends on the Babel patch on the ticket I said above, which, is not yet included into Babel's trunk.

  Changed 3 years ago by palgarvio

Updated patch.

Changed 3 years ago by palgarvio

  Changed 3 years ago by palgarvio

Updated patch to work with the latest branch changes.

  Changed 2 years ago by cboos

Current status is satisfying (r1092), the more advanced error reporting facilities can wait for a future release.

  Changed 2 years ago by cmlenz

  • milestone changed from 0.6 to 0.6.1

  Changed 22 months ago by cmlenz

  • milestone changed from 0.6.1 to 0.7

Add/Change #281 (advanced-18n: improve error reporting in case of extraction errors)

Author


E-mail address and user name can be saved in the Preferences.


Change Properties
<Author field>
Action
as new
as The resolution will be set. Next status will be 'closed'
to The owner will change from palgarvio. Next status will be 'new'
The owner will change from palgarvio to anonymous. Next status will be 'assigned'
 
Note: See TracTickets for help on using tickets.