From c485e236bd7e5ea40c64b2fe54d85dbb15b2fd39 Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Wed, 18 Mar 2009 16:55:59 +0000 Subject: [PATCH] Fixed #8193: all dynamic imports in Django are now done correctly. I know this because Brett Cannon borrowed the time machine and brought Python 2.7's '`importlib` back for inclusion in Django. Thanks for the patch-from-the-future, Brett! git-svn-id: http://code.djangoproject.com/svn/django/trunk@10088 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/conf/__init__.py | 6 ++-- django/contrib/admin/__init__.py | 5 +-- django/contrib/admin/views/template.py | 3 +- django/contrib/admindocs/views.py | 9 ++--- django/contrib/auth/__init__.py | 3 +- django/contrib/comments/__init__.py | 3 +- django/contrib/sessions/middleware.py | 3 +- django/core/cache/__init__.py | 6 ++-- django/core/files/storage.py | 3 +- django/core/files/uploadhandler.py | 3 +- django/core/handlers/base.py | 3 +- django/core/management/__init__.py | 16 ++++----- django/core/management/commands/flush.py | 3 +- django/core/management/commands/startapp.py | 3 +- .../core/management/commands/startproject.py | 3 +- django/core/management/commands/syncdb.py | 3 +- django/core/serializers/__init__.py | 3 +- django/core/servers/fastcgi.py | 6 ++-- django/core/urlresolvers.py | 7 ++-- django/db/__init__.py | 5 +-- django/db/models/loading.py | 15 ++++---- django/template/__init__.py | 3 +- django/template/context.py | 5 +-- django/template/loader.py | 3 +- django/template/loaders/app_directories.py | 11 ++---- django/templatetags/__init__.py | 3 +- django/test/client.py | 7 ++-- django/utils/importlib.py | 36 +++++++++++++++++++ django/utils/translation/trans_real.py | 10 ++---- django/views/debug.py | 4 ++- django/views/i18n.py | 5 +-- 32 files changed, 128 insertions(+), 71 deletions(-) create mode 100644 django/utils/importlib.py diff --git a/AUTHORS b/AUTHORS index 3e5fc66665a..0b11d3461fd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,6 +75,7 @@ answer newbie questions, and generally made Django that much better: Chris Cahoon Juan Manuel Caicedo Trevor Caira + Brett Cannon Ricardo Javier Cárdenes Medina Jeremy Carbaugh carljm diff --git a/django/conf/__init__.py b/django/conf/__init__.py index c980ee0b284..7bc7ae95083 100644 --- a/django/conf/__init__.py +++ b/django/conf/__init__.py @@ -12,6 +12,7 @@ import time # Needed for Windows from django.conf import global_settings from django.utils.functional import LazyObject +from django.utils import importlib ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" @@ -69,7 +70,7 @@ class Settings(object): self.SETTINGS_MODULE = settings_module try: - mod = __import__(self.SETTINGS_MODULE, {}, {}, ['']) + mod = importlib.import_module(self.SETTINGS_MODULE) except ImportError, e: raise ImportError, "Could not import settings '%s' (Is it on sys.path? Does it have syntax errors?): %s" % (self.SETTINGS_MODULE, e) @@ -89,7 +90,8 @@ class Settings(object): new_installed_apps = [] for app in self.INSTALLED_APPS: if app.endswith('.*'): - appdir = os.path.dirname(__import__(app[:-2], {}, {}, ['']).__file__) + app_mod = importlib.import_module(app[:-2]) + appdir = os.path.dirname(app_mod.__file__) app_subdirs = os.listdir(appdir) app_subdirs.sort() name_pattern = re.compile(r'[a-zA-Z]\w*') diff --git a/django/contrib/admin/__init__.py b/django/contrib/admin/__init__.py index bb856a1241f..b2eeeebb1e9 100644 --- a/django/contrib/admin/__init__.py +++ b/django/contrib/admin/__init__.py @@ -1,6 +1,7 @@ from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL from django.contrib.admin.options import StackedInline, TabularInline from django.contrib.admin.sites import AdminSite, site +from django.utils.importlib import import_module # A flag to tell us if autodiscover is running. autodiscover will set this to # True while running, and False when it finishes. @@ -36,7 +37,7 @@ def autodiscover(): # fails silently -- apps that do weird things with __path__ might # need to roll their own admin registration. try: - app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__ + app_path = import_module(app).__path__ except AttributeError: continue @@ -51,6 +52,6 @@ def autodiscover(): # Step 3: import the app's admin file. If this has errors we want them # to bubble up. - __import__("%s.admin" % app) + import_module("%s.admin" % app) # autodiscover was successful, reset loading flag. LOADING = False diff --git a/django/contrib/admin/views/template.py b/django/contrib/admin/views/template.py index de9320bfc2c..e1f64957c63 100644 --- a/django/contrib/admin/views/template.py +++ b/django/contrib/admin/views/template.py @@ -4,6 +4,7 @@ from django.template import loader from django.shortcuts import render_to_response from django.contrib.sites.models import Site from django.conf import settings +from django.utils.importlib import import_module from django.utils.translation import ugettext_lazy as _ @@ -15,7 +16,7 @@ def template_validator(request): # get a dict of {site_id : settings_module} for the validator settings_modules = {} for mod in settings.ADMIN_FOR: - settings_module = __import__(mod, {}, {}, ['']) + settings_module = import_module(mod) settings_modules[settings_module.SITE_ID] = settings_module site_list = Site.objects.in_bulk(settings_modules.keys()).values() if request.POST: diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py index 2ff2750da13..110a91f318e 100644 --- a/django/contrib/admindocs/views.py +++ b/django/contrib/admindocs/views.py @@ -9,6 +9,7 @@ from django.http import Http404 from django.core import urlresolvers from django.contrib.admindocs import utils from django.contrib.sites.models import Site +from django.utils.importlib import import_module from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe import inspect, os, re @@ -114,13 +115,13 @@ def view_index(request): return missing_docutils_page(request) if settings.ADMIN_FOR: - settings_modules = [__import__(m, {}, {}, ['']) for m in settings.ADMIN_FOR] + settings_modules = [import_module(m) for m in settings.ADMIN_FOR] else: settings_modules = [settings] views = [] for settings_mod in settings_modules: - urlconf = __import__(settings_mod.ROOT_URLCONF, {}, {}, ['']) + urlconf = import_module(settings_mod.ROOT_URLCONF) view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns) if Site._meta.installed: site_obj = Site.objects.get(pk=settings_mod.SITE_ID) @@ -146,7 +147,7 @@ def view_detail(request, view): mod, func = urlresolvers.get_mod_func(view) try: - view_func = getattr(__import__(mod, {}, {}, ['']), func) + view_func = getattr(import_module(mod), func) except (ImportError, AttributeError): raise Http404 title, body, metadata = utils.parse_docstring(view_func.__doc__) @@ -257,7 +258,7 @@ model_detail = staff_member_required(model_detail) def template_detail(request, template): templates = [] for site_settings_module in settings.ADMIN_FOR: - settings_mod = __import__(site_settings_module, {}, {}, ['']) + settings_mod = import_module(site_settings_module) if Site._meta.installed: site_obj = Site.objects.get(pk=settings_mod.SITE_ID) else: diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 87c2bc1b500..b89aee16822 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -1,5 +1,6 @@ import datetime from django.core.exceptions import ImproperlyConfigured +from django.utils.importlib import import_module SESSION_KEY = '_auth_user_id' BACKEND_SESSION_KEY = '_auth_user_backend' @@ -9,7 +10,7 @@ def load_backend(path): i = path.rfind('.') module, attr = path[:i], path[i+1:] try: - mod = __import__(module, {}, {}, [attr]) + mod = import_module(module) except ImportError, e: raise ImproperlyConfigured, 'Error importing authentication backend %s: "%s"' % (module, e) except ValueError, e: diff --git a/django/contrib/comments/__init__.py b/django/contrib/comments/__init__.py index b5c5acd2542..42384e786b6 100644 --- a/django/contrib/comments/__init__.py +++ b/django/contrib/comments/__init__.py @@ -3,6 +3,7 @@ from django.core import urlresolvers from django.core.exceptions import ImproperlyConfigured from django.contrib.comments.models import Comment from django.contrib.comments.forms import CommentForm +from django.utils.importlib import import_module DEFAULT_COMMENTS_APP = 'django.contrib.comments' @@ -18,7 +19,7 @@ def get_comment_app(): # Try to import the package try: - package = __import__(comments_app, '', '', ['']) + package = import_module(comments_app) except ImportError: raise ImproperlyConfigured("The COMMENTS_APP setting refers to "\ "a non-existing package.") diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index 0391e504006..57fcb9015a2 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -3,10 +3,11 @@ import time from django.conf import settings from django.utils.cache import patch_vary_headers from django.utils.http import cookie_date +from django.utils.importlib import import_module class SessionMiddleware(object): def process_request(self, request): - engine = __import__(settings.SESSION_ENGINE, {}, {}, ['']) + engine = import_module(settings.SESSION_ENGINE) session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None) request.session = engine.SessionStore(session_key) diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py index 23a61ef859a..739d3c48343 100644 --- a/django/core/cache/__init__.py +++ b/django/core/cache/__init__.py @@ -19,6 +19,7 @@ from cgi import parse_qsl from django.conf import settings from django.core import signals from django.core.cache.backends.base import InvalidCacheBackendError +from django.utils import importlib # Name for use in settings file --> name of module in "backends" directory. # Any backend scheme that is not in this dictionary is treated as a Python @@ -58,9 +59,10 @@ def parse_backend_uri(backend_uri): def get_cache(backend_uri): scheme, host, params = parse_backend_uri(backend_uri) if scheme in BACKENDS: - module = __import__('django.core.cache.backends.%s' % BACKENDS[scheme], {}, {}, ['']) + name = 'django.core.cache.backends.%s' % BACKENDS[scheme] else: - module = __import__(scheme, {}, {}, ['']) + name = scheme + module = importlib.import_module(name) return getattr(module, 'CacheClass')(host, params) cache = get_cache(settings.CACHE_BACKEND) diff --git a/django/core/files/storage.py b/django/core/files/storage.py index a1b843d5a11..bf30a787bbd 100644 --- a/django/core/files/storage.py +++ b/django/core/files/storage.py @@ -8,6 +8,7 @@ from django.core.files import locks, File from django.core.files.move import file_move_safe from django.utils.encoding import force_unicode from django.utils.functional import LazyObject +from django.utils.importlib import import_module from django.utils.text import get_valid_filename from django.utils._os import safe_join @@ -230,7 +231,7 @@ def get_storage_class(import_path=None): raise ImproperlyConfigured("%s isn't a storage module." % import_path) module, classname = import_path[:dot], import_path[dot+1:] try: - mod = __import__(module, {}, {}, ['']) + mod = import_module(module) except ImportError, e: raise ImproperlyConfigured('Error importing storage module %s: "%s"' % (module, e)) try: diff --git a/django/core/files/uploadhandler.py b/django/core/files/uploadhandler.py index b93ff9446e5..6a0eebe43ea 100644 --- a/django/core/files/uploadhandler.py +++ b/django/core/files/uploadhandler.py @@ -10,6 +10,7 @@ except ImportError: from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.files.uploadedfile import TemporaryUploadedFile, InMemoryUploadedFile +from django.utils import importlib __all__ = ['UploadFileException','StopUpload', 'SkipFile', 'FileUploadHandler', 'TemporaryFileUploadHandler', 'MemoryFileUploadHandler', @@ -201,7 +202,7 @@ def load_handler(path, *args, **kwargs): i = path.rfind('.') module, attr = path[:i], path[i+1:] try: - mod = __import__(module, {}, {}, [attr]) + mod = importlib.import_module(module) except ImportError, e: raise ImproperlyConfigured('Error importing upload handler module %s: "%s"' % (module, e)) except ValueError, e: diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index f1b69c59c22..e6ef6e2f9e9 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -3,6 +3,7 @@ import sys from django import http from django.core import signals from django.utils.encoding import force_unicode +from django.utils.importlib import import_module class BaseHandler(object): # Changes that are always applied to a response (in this order). @@ -36,7 +37,7 @@ class BaseHandler(object): raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:] try: - mod = __import__(mw_module, {}, {}, ['']) + mod = import_module(mw_module) except ImportError, e: raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e) try: diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index ec5ea19c980..4b40009e763 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -5,6 +5,7 @@ import imp import django from django.core.management.base import BaseCommand, CommandError, handle_default_options +from django.utils.importlib import import_module # For backwards compatibility: get_version() used to be in this module. get_version = django.get_version @@ -63,8 +64,8 @@ def load_command_class(app_name, name): class instance. All errors raised by the import process (ImportError, AttributeError) are allowed to propagate. """ - return getattr(__import__('%s.management.commands.%s' % (app_name, name), - {}, {}, ['Command']), 'Command')() + module = import_module('%s.management.commands.%s' % (app_name, name)) + return module.Command() def get_commands(): """ @@ -104,12 +105,9 @@ def get_commands(): # Find the project directory try: from django.conf import settings - project_directory = setup_environ( - __import__( - settings.SETTINGS_MODULE, {}, {}, - (settings.SETTINGS_MODULE.split(".")[-1],) - ), settings.SETTINGS_MODULE - ) + module = import_module(settings.SETTINGS_MODULE.split('.', 1)[0]) + project_directory = setup_environ(module, + settings.SETTINGS_MODULE) except (AttributeError, EnvironmentError, ImportError): project_directory = None @@ -328,7 +326,7 @@ def setup_environ(settings_mod, original_settings_path=None): # Import the project module. We add the parent directory to PYTHONPATH to # avoid some of the path errors new users can have. sys.path.append(os.path.join(project_directory, os.pardir)) - project_module = __import__(project_name, {}, {}, ['']) + project_module = import_module(project_name) sys.path.pop() return project_directory diff --git a/django/core/management/commands/flush.py b/django/core/management/commands/flush.py index f0df69204b9..5d8a36e7a21 100644 --- a/django/core/management/commands/flush.py +++ b/django/core/management/commands/flush.py @@ -1,5 +1,6 @@ from django.core.management.base import NoArgsCommand, CommandError from django.core.management.color import no_style +from django.utils.importlib import import_module from optparse import make_option class Command(NoArgsCommand): @@ -23,7 +24,7 @@ class Command(NoArgsCommand): # dispatcher events. for app_name in settings.INSTALLED_APPS: try: - __import__(app_name + '.management', {}, {}, ['']) + import_module('.management', app_name) except ImportError: pass diff --git a/django/core/management/commands/startapp.py b/django/core/management/commands/startapp.py index a81c4271425..13d64834a60 100644 --- a/django/core/management/commands/startapp.py +++ b/django/core/management/commands/startapp.py @@ -1,6 +1,7 @@ import os from django.core.management.base import copy_helper, CommandError, LabelCommand +from django.utils.importlib import import_module class Command(LabelCommand): help = "Creates a Django app directory structure for the given app name in the current directory." @@ -26,7 +27,7 @@ class Command(LabelCommand): # Check that the app_name cannot be imported. try: - __import__(app_name) + import_module(app_name) except ImportError: pass else: diff --git a/django/core/management/commands/startproject.py b/django/core/management/commands/startproject.py index 540a64d2ea9..712e43da830 100644 --- a/django/core/management/commands/startproject.py +++ b/django/core/management/commands/startproject.py @@ -1,4 +1,5 @@ from django.core.management.base import copy_helper, CommandError, LabelCommand +from django.utils.importlib import import_module import os import re from random import choice @@ -20,7 +21,7 @@ class Command(LabelCommand): # Check that the project_name cannot be imported. try: - __import__(project_name) + import_module(project_name) except ImportError: pass else: diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index dbef7a6a959..fe51d45bb36 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -1,5 +1,6 @@ from django.core.management.base import NoArgsCommand from django.core.management.color import no_style +from django.utils.importlib import import_module from optparse import make_option import sys @@ -30,7 +31,7 @@ class Command(NoArgsCommand): # dispatcher events. for app_name in settings.INSTALLED_APPS: try: - __import__(app_name + '.management', {}, {}, ['']) + import_module('.management', app_name) except ImportError, exc: # This is slightly hackish. We want to ignore ImportErrors # if the "management" module itself is missing -- but we don't diff --git a/django/core/serializers/__init__.py b/django/core/serializers/__init__.py index 5365efeaccc..1a2eb4f6cc3 100644 --- a/django/core/serializers/__init__.py +++ b/django/core/serializers/__init__.py @@ -17,6 +17,7 @@ To add your own serializers, use the SERIALIZATION_MODULES setting:: """ from django.conf import settings +from django.utils import importlib # Built-in serializers BUILTIN_SERIALIZERS = { @@ -47,7 +48,7 @@ def register_serializer(format, serializer_module, serializers=None): directly into the global register of serializers. Adding serializers directly is not a thread-safe operation. """ - module = __import__(serializer_module, {}, {}, ['']) + module = importlib.import_module(serializer_module) if serializers is None: _serializers[format] = module else: diff --git a/django/core/servers/fastcgi.py b/django/core/servers/fastcgi.py index dc4c35b08dc..b554095bce6 100644 --- a/django/core/servers/fastcgi.py +++ b/django/core/servers/fastcgi.py @@ -12,6 +12,7 @@ Run with the extra option "help" for a list of additional options you can pass to this server. """ +from django.utils import importlib import sys, os __version__ = "0.1" @@ -113,7 +114,7 @@ def runfastcgi(argset=[], **kwargs): 'maxSpare': int(options["maxspare"]), 'minSpare': int(options["minspare"]), 'maxChildren': int(options["maxchildren"]), - 'maxRequests': int(options["maxrequests"]), + 'maxRequests': int(options["maxrequests"]), } flup_module += '_fork' elif options['method'] in ('thread', 'threaded'): @@ -128,7 +129,8 @@ def runfastcgi(argset=[], **kwargs): wsgi_opts['debug'] = False # Turn off flup tracebacks try: - WSGIServer = getattr(__import__('flup.' + flup_module, '', '', flup_module), 'WSGIServer') + module = importlib_import_module('.%s' % flup_module, 'flup') + WSGIServer = module.WSGIServer except: print "Can't import flup." + flup_module return False diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index fba6bb22e8e..6fc5cfce740 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -14,6 +14,7 @@ from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist from django.utils.datastructures import MultiValueDict from django.utils.encoding import iri_to_uri, force_unicode, smart_str from django.utils.functional import memoize +from django.utils.importlib import import_module from django.utils.regex_helper import normalize from django.utils.thread_support import currentThread @@ -54,7 +55,7 @@ def get_callable(lookup_view, can_fail=False): lookup_view = lookup_view.encode('ascii') mod_name, func_name = get_mod_func(lookup_view) if func_name != '': - lookup_view = getattr(__import__(mod_name, {}, {}, ['']), func_name) + lookup_view = getattr(import_module(mod_name), func_name) if not callable(lookup_view): raise AttributeError("'%s.%s' is not a callable." % (mod_name, func_name)) except (ImportError, AttributeError): @@ -199,7 +200,7 @@ class RegexURLResolver(object): try: return self._urlconf_module except AttributeError: - self._urlconf_module = __import__(self.urlconf_name, {}, {}, ['']) + self._urlconf_module = import_module(self.urlconf_name) return self._urlconf_module urlconf_module = property(_get_urlconf_module) @@ -217,7 +218,7 @@ class RegexURLResolver(object): callback = getattr(self.urlconf_module, 'handler%s' % view_type) mod_name, func_name = get_mod_func(callback) try: - return getattr(__import__(mod_name, {}, {}, ['']), func_name), {} + return getattr(import_module(mod_name), func_name), {} except (ImportError, AttributeError), e: raise ViewDoesNotExist, "Tried %s. Error was: %s" % (callback, str(e)) diff --git a/django/db/__init__.py b/django/db/__init__.py index da1544f8cd1..dde2f2eb228 100644 --- a/django/db/__init__.py +++ b/django/db/__init__.py @@ -3,6 +3,7 @@ from django.conf import settings from django.core import signals from django.core.exceptions import ImproperlyConfigured from django.utils.functional import curry +from django.utils.importlib import import_module __all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError') @@ -13,12 +14,12 @@ def load_backend(backend_name): try: # Most of the time, the database backend will be one of the official # backends that ships with Django, so look there first. - return __import__('django.db.backends.%s.base' % backend_name, {}, {}, ['']) + return import_module('.base', 'django.db.backends.%s' % settings.DATABASE_ENGINE) except ImportError, e: # If the import failed, we might be looking for a database backend # distributed external to Django. So we'll try that next. try: - return __import__('%s.base' % backend_name, {}, {}, ['']) + return import_module('.base', backend_name) except ImportError, e_user: # The database backend wasn't found. Display a helpful error message # listing all possible (built-in) database backends. diff --git a/django/db/models/loading.py b/django/db/models/loading.py index 6837e070aca..e07aab4efef 100644 --- a/django/db/models/loading.py +++ b/django/db/models/loading.py @@ -3,6 +3,7 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.utils.datastructures import SortedDict +from django.utils.importlib import import_module import sys import os @@ -69,9 +70,10 @@ class AppCache(object): """ self.handled[app_name] = None self.nesting_level += 1 - mod = __import__(app_name, {}, {}, ['models']) - self.nesting_level -= 1 - if not hasattr(mod, 'models'): + try: + models = import_module('.models', app_name) + except ImportError: + self.nesting_level -= 1 if can_postpone: # Either the app has no models, or the package is still being # imported by Python and the model module isn't available yet. @@ -79,9 +81,10 @@ class AppCache(object): # populate). self.postponed.append(app_name) return None - if mod.models not in self.app_store: - self.app_store[mod.models] = len(self.app_store) - return mod.models + self.nesting_level -= 1 + if models not in self.app_store: + self.app_store[models] = len(self.app_store) + return models def app_cache_ready(self): """ diff --git a/django/template/__init__.py b/django/template/__init__.py index bdc4b890d12..d008b7f8741 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -52,6 +52,7 @@ import re from inspect import getargspec from django.conf import settings from django.template.context import Context, RequestContext, ContextPopException +from django.utils.importlib import import_module from django.utils.itercompat import is_iterable from django.utils.functional import curry, Promise from django.utils.text import smart_split @@ -935,7 +936,7 @@ def get_library(module_name): lib = libraries.get(module_name, None) if not lib: try: - mod = __import__(module_name, {}, {}, ['']) + mod = import_module(module_name) except ImportError, e: raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e)) try: diff --git a/django/template/context.py b/django/template/context.py index 8f16a950217..1f136595e17 100644 --- a/django/template/context.py +++ b/django/template/context.py @@ -1,5 +1,6 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured +from django.utils.importlib import import_module _standard_context_processors = None @@ -62,7 +63,7 @@ class Context(object): def update(self, other_dict): "Like dict.update(). Pushes an entire dictionary's keys and values onto the context." - if not hasattr(other_dict, '__getitem__'): + if not hasattr(other_dict, '__getitem__'): raise TypeError('other_dict must be a mapping (dictionary-like) object.') self.dicts = [other_dict] + self.dicts return other_dict @@ -77,7 +78,7 @@ def get_standard_processors(): i = path.rfind('.') module, attr = path[:i], path[i+1:] try: - mod = __import__(module, {}, {}, [attr]) + mod = import_module(module) except ImportError, e: raise ImproperlyConfigured('Error importing request processor module %s: "%s"' % (module, e)) try: diff --git a/django/template/loader.py b/django/template/loader.py index 1d7d945ef73..8195c4b7982 100644 --- a/django/template/loader.py +++ b/django/template/loader.py @@ -22,6 +22,7 @@ from django.core.exceptions import ImproperlyConfigured from django.template import Origin, Template, Context, TemplateDoesNotExist, add_to_builtins +from django.utils.importlib import import_module from django.conf import settings template_source_loaders = None @@ -51,7 +52,7 @@ def find_template_source(name, dirs=None): i = path.rfind('.') module, attr = path[:i], path[i+1:] try: - mod = __import__(module, globals(), locals(), [attr]) + mod = import_module(module) except ImportError, e: raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e) try: diff --git a/django/template/loaders/app_directories.py b/django/template/loaders/app_directories.py index 975b8ad95fa..b5ae602666d 100644 --- a/django/template/loaders/app_directories.py +++ b/django/template/loaders/app_directories.py @@ -10,21 +10,14 @@ from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.template import TemplateDoesNotExist from django.utils._os import safe_join +from django.utils.importlib import import_module # At compile time, cache the directories to search. fs_encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() app_template_dirs = [] for app in settings.INSTALLED_APPS: - i = app.rfind('.') - if i == -1: - m, a = app, None - else: - m, a = app[:i], app[i+1:] try: - if a is None: - mod = __import__(m, {}, {}, []) - else: - mod = getattr(__import__(m, {}, {}, [a]), a) + mod = import_module(app) except ImportError, e: raise ImproperlyConfigured, 'ImportError %s: %s' % (app, e.args[0]) template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates') diff --git a/django/templatetags/__init__.py b/django/templatetags/__init__.py index 9204535abbc..4033e5a8f63 100644 --- a/django/templatetags/__init__.py +++ b/django/templatetags/__init__.py @@ -1,7 +1,8 @@ from django.conf import settings +from django.utils import importlib for a in settings.INSTALLED_APPS: try: - __path__.extend(__import__(a + '.templatetags', {}, {}, ['']).__path__) + __path__.extend(importlib.import_module('.templatetags', a).__path__) except ImportError: pass diff --git a/django/test/client.py b/django/test/client.py index 6c5f4ff4a94..b1421901e27 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -18,6 +18,7 @@ from django.test import signals from django.utils.functional import curry from django.utils.encoding import smart_str from django.utils.http import urlencode +from django.utils.importlib import import_module from django.utils.itercompat import is_iterable from django.db import transaction, close_connection from django.test.utils import ContextList @@ -176,7 +177,7 @@ class Client(object): Obtains the current session variables. """ if 'django.contrib.sessions' in settings.INSTALLED_APPS: - engine = __import__(settings.SESSION_ENGINE, {}, {}, ['']) + engine = import_module(settings.SESSION_ENGINE) cookie = self.cookies.get(settings.SESSION_COOKIE_NAME, None) if cookie: return engine.SessionStore(cookie.value) @@ -399,7 +400,7 @@ class Client(object): user = authenticate(**credentials) if user and user.is_active \ and 'django.contrib.sessions' in settings.INSTALLED_APPS: - engine = __import__(settings.SESSION_ENGINE, {}, {}, ['']) + engine = import_module(settings.SESSION_ENGINE) # Create a fake request to store login details. request = HttpRequest() @@ -434,7 +435,7 @@ class Client(object): Causes the authenticated user to be logged out. """ - session = __import__(settings.SESSION_ENGINE, {}, {}, ['']).SessionStore() + session = import_module(settings.SESSION_ENGINE).SessionStore() session.delete(session_key=self.cookies[settings.SESSION_COOKIE_NAME].value) self.cookies = SimpleCookie() diff --git a/django/utils/importlib.py b/django/utils/importlib.py new file mode 100644 index 00000000000..ef4d0e4a276 --- /dev/null +++ b/django/utils/importlib.py @@ -0,0 +1,36 @@ +# Taken from Python 2.7 with permission from/by the original author. +import sys + +def _resolve_name(name, package, level): + """Return the absolute name of the module to be imported.""" + if not hasattr(package, 'rindex'): + raise ValueError("'package' not set to a string") + dot = len(package) + for x in xrange(level, 1, -1): + try: + dot = package.rindex('.', 0, dot) + except ValueError: + raise ValueError("attempted relative import beyond top-level " + "package") + return "%s.%s" % (package[:dot], name) + + +def import_module(name, package=None): + """Import a module. + + The 'package' argument is required when performing a relative import. It + specifies the package to use as the anchor point from which to resolve the + relative import to an absolute import. + + """ + if name.startswith('.'): + if not package: + raise TypeError("relative imports require the 'package' argument") + level = 0 + for character in name: + if character != '.': + break + level += 1 + name = _resolve_name(name[level:], package, level) + __import__(name) + return sys.modules[name] diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py index 7a0e104b37e..48ed7cc8854 100644 --- a/django/utils/translation/trans_real.py +++ b/django/utils/translation/trans_real.py @@ -7,6 +7,7 @@ import sys import gettext as gettext_module from cStringIO import StringIO +from django.utils.importlib import import_module from django.utils.safestring import mark_safe, SafeData from django.utils.thread_support import currentThread @@ -125,7 +126,7 @@ def translation(language): if settings.SETTINGS_MODULE is not None: parts = settings.SETTINGS_MODULE.split('.') - project = __import__(parts[0], {}, {}, []) + project = import_module(parts[0]) projectpath = os.path.join(os.path.dirname(project.__file__), 'locale') else: projectpath = None @@ -176,12 +177,7 @@ def translation(language): res = _merge(projectpath) for appname in settings.INSTALLED_APPS: - p = appname.rfind('.') - if p >= 0: - app = getattr(__import__(appname[:p], {}, {}, [appname[p+1:]]), appname[p+1:]) - else: - app = __import__(appname, {}, {}, []) - + app = import_module(appname) apppath = os.path.join(os.path.dirname(app.__file__), 'locale') if os.path.isdir(apppath): diff --git a/django/views/debug.py b/django/views/debug.py index d1e92592436..f2288cf1731 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -6,6 +6,7 @@ import datetime from django.conf import settings from django.template import Template, Context, TemplateDoesNotExist from django.utils.html import escape +from django.utils.importlib import import_module from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound from django.utils.encoding import smart_unicode, smart_str @@ -67,7 +68,8 @@ class ExceptionReporter: self.loader_debug_info = [] for loader in template_source_loaders: try: - source_list_func = getattr(__import__(loader.__module__, {}, {}, ['get_template_sources']), 'get_template_sources') + module = import_module(loader.__module__) + source_list_func = module.get_template_sources # NOTE: This assumes exc_value is the name of the template that # the loader attempted to load. template_list = [{'name': t, 'exists': os.path.exists(t)} \ diff --git a/django/views/i18n.py b/django/views/i18n.py index e141c1d967b..0280698aaea 100644 --- a/django/views/i18n.py +++ b/django/views/i18n.py @@ -1,7 +1,8 @@ from django import http +from django.conf import settings +from django.utils import importlib from django.utils.translation import check_for_language, activate, to_locale, get_language from django.utils.text import javascript_quote -from django.conf import settings import os import gettext as gettext_module @@ -128,7 +129,7 @@ def javascript_catalog(request, domain='djangojs', packages=None): paths = [] # first load all english languages files for defaults for package in packages: - p = __import__(package, {}, {}, ['']) + p = importlib.import_module(package) path = os.path.join(os.path.dirname(p.__file__), 'locale') paths.append(path) try: