Edgewall Software

Opened 15 years ago

Last modified 7 years ago

#281 new defect

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

Reported by: cboos Owned by: palgarvio
Priority: major Milestone: 0.9
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 (3)

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

Download all attachments as: .zip

Change History (17)

Changed 15 years ago by cboos

move all the raise IndexError statements in one place

Changed 15 years ago by cboos

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

comment:1 follow-up: Changed 15 years ago by palgarvio

Included attachment:refactor-error-reporting-for-extraction-errors-r967.diff 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.

comment:2 in reply to: ↑ 1 ; follow-up: Changed 15 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 ;-)

comment:3 in reply to: ↑ 2 Changed 15 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:

  • 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 ;-)

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.

comment:4 Changed 15 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 :)

comment:5 Changed 15 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?

comment:6 follow-up: Changed 15 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.

comment:7 in reply to: ↑ 6 Changed 15 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.

comment:8 Changed 15 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.

comment:9 Changed 15 years ago by palgarvio

Updated patch.

Changed 15 years ago by palgarvio

comment:10 Changed 15 years ago by palgarvio

Updated patch to work with the latest branch changes.

comment:11 Changed 14 years ago by cboos

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

comment:12 Changed 14 years ago by cmlenz

  • Milestone changed from 0.6 to 0.6.1

comment:13 Changed 14 years ago by cmlenz

  • Milestone changed from 0.6.1 to 0.7

comment:14 Changed 7 years ago by hodgestar

  • Milestone changed from 0.7 to 0.9

Moved to milestone 0.9.

Note: See TracTickets for help on using tickets.