#15084: Unnecessary imports lead to ImportError
--------------------------------------+------------------------------------
Reporter: vanschelven | Owner: nobody
Type: Bug | Status: reopened
Component: Internationalization | Version: 1.4
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 0 | Needs documentation: 0
Needs tests: 1 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
--------------------------------------+------------------------------------
Changes (by vanschelven):
* status: closed => reopened
* version: 1.2 => 1.4
* resolution: needsinfo =>
Comment:
Ok, I'm reopening this as I finally acquired the understanding of what's
really going wrong and managed to create an "easily" reproducible scenario
(not so much test; but that's hard in the case of the current global app-
loading anyway).
Create 2 apps.
{{{
# defining_app/__init__.py
from django.utils.translation import ugettext
class TranslatedClass(object):
value = ugettext("Field")
}}}
{{{
# using_app/__init__.py
from defining_app import TranslatedClass
}}}
Add them to INSTALLED_APPS as such (the order is important):
{{{
# settings.py
INSTALLED_APPS = (
# ...
'using_app',
'defining_app',
)
}}}
This will fail in Django up to 1.4; but succeed with the patch provided by
Jeff.
Why?
* Pretty much any Django command calls activate() somewhere. This will
trigger the reverse loop over the INSTALLED_APPS. and will start importing
apps
(https://github.com/django/django/blob/1.4/django/utils/translation/trans_real.py#L159)
* defining_app is at the bottom of the list; it will be imported
* When evaluating 'value = uggettext("Field")' we go into another reverse
loop over INSTALLED_APPS as dictated by trans_real.py
* By now 'defining_app' is in sys.modules, so that import statement
doesn't trigger further evaluations
* Next up in the loop: using_app.
* using_app has an "from defining_app import !TranslatedClass" statement;
however, since we're still somewhere on the stack of getting 'value',
!TranslatedClass is not available yet.
* !ImportError.
In other words, the current implementation of uggettext sneaks in a
dependency/call to all installed apps' __init__.py files. Additionally,
because the import happens half-way in a class definition (a rather usual
pattern in Django) the class definition cannot be finished in time for the
using_module's code to access it.
Why the solution works is simple: not importing modules at (for the user
of Django) seemingly random moments will remove the dependency, and allow
for !TranslatedClass to be added to the module's dictionary of available
variables.
I hope this reopens the issue for renewed attention. I _do_ consider this
a serious issue, even though apparently not many people have found their
way to this page or found a way to isolate the problem. I'd consider doing
unasked-for imports something we need to do as little as possible (a.k.a.
bad engineering). Especially the fact that uggettext() & friends may
trigger this makes this a hairy bug: uggettext is often used in the middle
of a module or class definition.
As to the solution: I'm not sure about the corner cases but willing to
help out. I'll start by providing a patch that deals with the known return
values for get_loader.
--
Ticket URL: <https://code.djangoproject.com/ticket/15084#comment:20>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit https://groups.google.com/groups/opt_out.