#8033 closed Cleanup/optimization (fixed)
Better error handling for gettext and gettext_lazy issues
Reported by: | Manuel Saelices | Owned by: | |
---|---|---|---|
Component: | Internationalization | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
It's very common get confused in use of gettext
and gettext_lazy
functions. An usual error was to use gettext
at import time, for example, in any of this cases:
- In a parameter of
settings.py
file:from django.utils.translation import ugettext as _ ... LANGUAGE_CHOICES = (('es', _('Spanish')), ('en', _('English')),)
- In class or field definition of a
models.py
file:from django.db import models from django.utils.translation import ugettext as _ class Author(models.Model): name = models.CharField(_(u'Name'), max_length=100)
In both cases, if you get an error, the error will be an ImportError
usually in another part of code... This is very difficult to debug, because the real error should be "Incorrect use of gettext. You can't use gettext at import time".
The error is due to the fact of not all applications has been imported yet, and gettext
resolution need to have all possible translation files loaded (django.po
files), which implies loading of all applications. This causes a circular import error problem.
In this thread there is a discussion about this.
We are thinking in check in every call to gettext
if django apps has been loaded (using django.db.models.loading.app_cache_ready()
) and take one of this options:
- raise an representative error.
- return a dummy string.
I personally prefers first one, because gettext
should be always called inside functions (at run time), and never at import time. I don't imagine a case of we need use gettext
before that.
Attachments (5)
Change History (35)
comment:1 by , 16 years ago
follow-up: 6 comment:2 by , 16 years ago
I've found that previous real error was: TemplateDoesNotExist: 500.html
but in DEBUG mode, fails in variables and context rendering.
by , 16 years ago
Attachment: | better_error_handling_for_ugettext_r8138.2.diff added |
---|
A better patch... but It's seems to have problems in apache detection of app_cache_ready
by , 16 years ago
Attachment: | better_error_handling_for_ugettext_r8138.diff added |
---|
A better patch... but It's seems to have problems in apache detection of app_cache_ready
comment:3 by , 16 years ago
Now, the problem I get in apache is that always raise error, even when call gettext
at run time. But the problem seems to be that app_cache_ready()
doesnt work in Apache. Always returns False
, even inside a django view.
comment:4 by , 16 years ago
I think I find an error with apps installation. I don't find any code point that forces a django apps loading, and apps loading occurs if some things happends, like gettext
call. For example, if in your first line of your django view, in a production server with mod_python
, you call to django.db.models.loading.app_cache_ready()
you get False
.
I will attach a patch that fixes this problem, but changes Django core parts (django.core.handlers.base
module). Ok, I can say that this core changes passes all tests and works in my dev and production server.
by , 16 years ago
Attachment: | better_error_handling_for_ugettext_r8138.3.diff added |
---|
This patch fixes apache problems, forcing apps loading just in request processing
follow-up: 8 comment:5 by , 16 years ago
Owner: | changed from | to
---|---|
Triage Stage: | Unreviewed → Accepted |
This is pretty evil. :-(
We intentionally do lazy apps loading for a reason (but I cannot, for the life of me, remember why that is). I'll try to remember the reason, but I don't like forcing it like this. It's certainly not a bug that it happens lazily. I'd prefer to work around requiring it to be loaded. All that being said, your patch is a good start, so I'll have a read of it tonight.
follow-up: 7 comment:6 by , 16 years ago
Replying to msaelices:
I've found that previous real error was:
TemplateDoesNotExist: 500.html
but in DEBUG mode, fails in variables and context rendering.
The final problem was Django tries to translate server_time
to a spanish date, for rendering 500 template, in django.core.debug
module. For fix that, I deactivated all translation in 500 error handling and I checked in do_translate
that translations are activated, before raise an ImproperlyImplemented
exception if is the case.
comment:7 by , 16 years ago
Replying to msaelices:
Replying to msaelices:
I've found that previous real error was:
TemplateDoesNotExist: 500.html
but in DEBUG mode, fails in variables and context rendering.
The final problem was Django tries to translate
server_time
to a spanish date, for rendering 500 template, indjango.core.debug
module. For fix that, I deactivated all translation in 500 error handling and I checked indo_translate
that translations are activated, before raise anImproperlyImplemented
exception if is the case.
do you refer to do in do_translate
something like this?:
from django.db.models.loading import get_apps def do_translate(message, translation_function): global _default, _active t = _active.get(currentThread(), None) try: get_apps() except ImportError: raise ImproperlyImplemented( "ImportError occurs, maybe it was caused because" "django.utils.translation.%(func_name)s function was called at " "import time to perform a translation of '%(message)s'. " "%(func_name)s function must be called after " "applications loading." % {'func_name': translation_function, 'message': message})
comment:8 by , 16 years ago
Replying to mtredinnick:
This is pretty evil. :-(
We intentionally do lazy apps loading for a reason (but I cannot, for the life of me, remember why that is). I'll try to remember the reason, but I don't like forcing it like this. It's certainly not a bug that it happens lazily. I'd prefer to work around requiring it to be loaded. All that being said, your patch is a good start, so I'll have a read of it tonight.
Sorry for my previous comment... was in bad thread.
About your work around... do you refer to do in do_translate something like this?:
from django.db.models.loading import get_apps def do_translate(message, translation_function): global _default, _active t = _active.get(currentThread(), None) try: get_apps() except ImportError: raise ImproperlyImplemented( "ImportError occurs, maybe it was caused because" "django.utils.translation.%(func_name)s function was called at " "import time to perform a translation of '%(message)s'. " "%(func_name)s function must be called after " "applications loading." % {'func_name': translation_function, 'message': message}) ...
comment:9 by , 16 years ago
And with respect lazy apps loading. I dont mean is a bug, but the fail may be in let apps rely on app_cache_ready
, because maybe never get loaded. Maybe the reason for lazy apps loading was performance. If you count time of loading templatetags, etc. When you do a simple HttpResponseRedirect, may not have to import any apps.
comment:10 by , 16 years ago
@msaelices: how are you checking things with this patch? I realise it's pretty hard to write automated tests and I'm not really worried about that. I just want to make sure I'm covering all the possibilities you are when I make changes. So I'm using ugettext()
in the __init__.py
file of an app and in a model field definition. Anything else?
comment:11 by , 16 years ago
It's very strange thing. I dont get to make fails django trunk, but I remember fixed several related issues by gettext_lazy
. We also take a one of my company project and test it with actual django version and works. Also I try to update to a previous django revision (r8100) and works too.
What happening?
I will attach my test project, with uses gettext
in forms.py
and models.py
, with two apps inside and one uses the other.
comment:12 by , 16 years ago
Has patch: | set |
---|---|
Patch needs improvement: | set |
I haven't had as much time to look at this as I would have liked, but I just gave it another read. My feeling is that it is over-engineered. Firstly, it's not always wrong to use ugettext()
in a module at import time (because of dynamic import possibilities), but it usually is.
I also feel that the patch is trying to solve too large a problem. The real problem that we want to avoid is calling the translation machinery whilst apps are being imported. We have a way to tell when the app loading is finished. So probably all that we need is a way to tell is the app loading is in progress. How about adding an extra method/flag to the AppCache
class to indicate that loading is in progress? Then we can test that before trying to access a translation and, if it's true, raise an error. The number of lines of code should be less and it catches the really nasty problem case.
Adding lots and lots of lines of code to handle an error situation that is a developer error is adding complexity that shouldn't be necessary. A clear error message to describe the problem is good, though, but I think we've gone a bit too far in trying to hold the hand of the developer in this patch.
comment:13 by , 16 years ago
You're completely right. I'll try to make a better patch with your idea.
by , 16 years ago
Attachment: | better_error_handling_for_ugettext_r8377.diff added |
---|
Using a flag for no try translation while django apps is still loading
comment:14 by , 16 years ago
@mtredinnick, I added a patch with your idea. But, I think we do not solve all kind of problems. Still there is a possibility to call ugettext
before first app was tried to load. In this case, ugettext
was no check any problem, and a circular import error will occurs (because this translation would begin apps loading).
What is your opinion?
comment:15 by , 16 years ago
milestone: | → 1.0 maybe |
---|
comment:16 by , 16 years ago
milestone: | 1.0 maybe → 1.0 |
---|
comment:17 by , 16 years ago
milestone: | 1.0 → post-1.0 |
---|
This can wait until after 1.0. The patch still needs some work (we're aren't going to raise an exception saying Django isn't properly implemented, since that isn't true: this feature is about errors in the user's code). I'm also fairly sure it would be safe to use ugettext()
before importing all models and that could be a genuine use-case (e.g. in cronjobs), but it needs testing and mistakes now would be stabilising.
Since this is only a diagnostic check and only triggered for code that is already broken, we can push it to later.
comment:18 by , 16 years ago
Owner: | removed |
---|
comment:20 by , 14 years ago
Severity: | → Normal |
---|---|
Type: | → New feature |
comment:23 by , 11 years ago
Patch needs improvement: | unset |
---|---|
Type: | New feature → Cleanup/optimization |
I think that after app loading refactor, the situation has improved a lot.
However, here is a patch to add a complementary error message to this sort of errors: https://github.com/django/django/pull/2838
comment:24 by , 11 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:25 by , 11 years ago
Triage Stage: | Ready for checkin → Accepted |
---|
If we're creating a specific exception for the app registry not being ready, it has no reason to inherit RuntimeError.
Furthermore, this patch changes the error message without updating the docs — which are important as this is going to be one of the pain points in 1.7.
comment:26 by , 11 years ago
Upon further thought, I'm in favor of introducing AppRegistryNotReady(Exception)
and updating the documentation accordingly.
comment:28 by , 11 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
comment:29 by , 11 years ago
Owner: | set to |
---|---|
Resolution: | → fixed |
Status: | new → closed |
Previous patch pass all tests, but if you test in an apache deployment, it fails in rendering of 500 template, with this error: