Opened 16 years ago
Last modified 8 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:
- the error reporting could benefit from some code refactoring (attachment:refactor-error-reporting-for-extraction-errors-r967.diff)
- that error shouldn't actually be an error but only a warning, as one can easily produce automatic parameter names (attachment:warn-for-missing-parameters.patch). That patch applies on top of the former, and shouldn't be committed as such, as it should be better integrated with Babel. Currently the 'babel' logger is only present when Babel is used from the command line, not when called from the setup.py script.
Attachments (3)
Change History (17)
Changed 16 years ago by cboos
Changed 16 years ago by cboos
don't raise an IndexError but simply warn about sub-optimal use of the directive
comment:1 follow-up: ↓ 2 Changed 16 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: ↓ 3 Changed 16 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 108 109 109 110 <table py:if="properties or file" id="info" summary="Revision info"> 110 111 <tr py:if="file"> 111 <th scope="col" >112 <th scope="col" i18n:msg=""> 112 113 Revision <a href="${href.changeset(rev)}">$rev</a>, ${sizeinfo(file.size)} 113 114 (checked in by ${authorinfo(file.changeset.author)}, ${dateinfo(file.changeset.date)} ago) 114 115 </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 947 947 elif kind is EXPR: 948 948 if self.params: 949 949 param = self.params.pop(0) 950 el se:950 elif self.orig_params: 951 951 raise IndexError("'%s' parameters given to 'i18n:%s' but more " 952 952 "expressions used in '%s', line %s" % ( 953 953 ', '.join(self.orig_params), 954 954 self.directive.tagname, 955 955 os.path.basename(pos[0]), pos[1])) 956 else: 957 param = 'expr%d' % len(self.values) 956 958 self.string.append('%%(%s)s' % param) 957 959 self.events.setdefault(self.stack[-1], []).append(None) 958 960 self.values[param] = (kind, data, pos)
But ideally still without a traceback ;-)
comment:3 in reply to: ↑ 2 Changed 16 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 108 109 109 110 <table py:if="properties or file" id="info" summary="Revision info"> 110 111 <tr py:if="file"> 111 <th scope="col" >112 <th scope="col" i18n:msg=""> 112 113 Revision <a href="${href.changeset(rev)}">$rev</a>, ${sizeinfo(file.size)} 113 114 (checked in by ${authorinfo(file.changeset.author)}, ${dateinfo(file.changeset.date)} ago) 114 115 </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 947 947 elif kind is EXPR: 948 948 if self.params: 949 949 param = self.params.pop(0) 950 el se:950 elif self.orig_params: 951 951 raise IndexError("'%s' parameters given to 'i18n:%s' but more " 952 952 "expressions used in '%s', line %s" % ( 953 953 ', '.join(self.orig_params), 954 954 self.directive.tagname, 955 955 os.path.basename(pos[0]), pos[1])) 956 else: 957 param = 'expr%d' % len(self.values) 956 958 self.string.append('%%(%s)s' % param) 957 959 self.events.setdefault(self.stack[-1], []).append(None) 958 960 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 16 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 16 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
===================================================================
18 18 :note: Directives support added since version 0.6 19 19 """ 20 20 21 from distutils import log 21 22 from compiler import ast 22 23 from gettext import NullTranslations 23 24 import os … … 947 948 if self.params: 948 949 param = self.params.pop(0) 949 950 else: 951 import sys 950 952 params = ', '.join(['"%s"' % p for p in self.orig_params if p]) 951 953 if params: 952 954 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
947 947 if self.params: 948 948 param = self.params.pop(0) 949 949 else: 950 import sys 950 951 params = ', '.join(['"%s"' % p for p in self.orig_params if p]) 951 952 if params: 952 953 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])) 961 961 self.string.append('%%(%s)s' % param) 962 962 self.events.setdefault(self.stack[-1], []).append(None) 963 963 self.values[param] = (kind, data, pos)
Which one would you prefer?
comment:6 follow-up: ↓ 7 Changed 16 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 16 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 16 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 16 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 15 years ago by cboos
Current status is satisfying (r1092), the more advanced error reporting facilities can wait for a future release.
comment:12 Changed 15 years ago by cmlenz
- Milestone changed from 0.6 to 0.6.1
comment:13 Changed 15 years ago by cmlenz
- Milestone changed from 0.6.1 to 0.7
comment:14 Changed 8 years ago by hodgestar
- Milestone changed from 0.7 to 0.9
Moved to milestone 0.9.
move all the raise IndexError statements in one place