#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.


Reply via email to