Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactor all uses of thread locals to be more consistant and sane.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15232 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit fcbf881d8200f3771dd07be63e94171dd9548b92 1 parent 964cf1b
@alex alex authored
View
24 django/core/urlresolvers.py
@@ -8,6 +8,7 @@
"""
import re
+from threading import local
from django.http import Http404
from django.conf import settings
@@ -17,7 +18,6 @@
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
_resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances.
_callable_cache = {} # Maps view and url pattern names to their view functions.
@@ -25,10 +25,11 @@
# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
# the current thread (which is the only one we ever access), it is assumed to
# be empty.
-_prefixes = {}
+_prefixes = local()
# Overridden URLconfs for each thread are stored here.
-_urlconfs = {}
+_urlconfs = local()
+
class ResolverMatch(object):
def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=None):
@@ -401,7 +402,7 @@ def set_script_prefix(prefix):
"""
if not prefix.endswith('/'):
prefix += '/'
- _prefixes[currentThread()] = prefix
+ _prefixes.value = prefix
def get_script_prefix():
"""
@@ -409,27 +410,22 @@ def get_script_prefix():
wishes to construct their own URLs manually (although accessing the request
instance is normally going to be a lot cleaner).
"""
- return _prefixes.get(currentThread(), u'/')
+ return getattr(_prefixes, "value", u'/')
def set_urlconf(urlconf_name):
"""
Sets the URLconf for the current thread (overriding the default one in
settings). Set to None to revert back to the default.
"""
- thread = currentThread()
if urlconf_name:
- _urlconfs[thread] = urlconf_name
+ _urlconfs.value = urlconf_name
else:
- # faster than wrapping in a try/except
- if thread in _urlconfs:
- del _urlconfs[thread]
+ if hasattr(_urlconfs, "value"):
+ del _urlconfs.value
def get_urlconf(default=None):
"""
Returns the root URLconf to use for the current thread if it has been
changed from the default one.
"""
- thread = currentThread()
- if thread in _urlconfs:
- return _urlconfs[thread]
- return default
+ return getattr(_urlconfs, "value", default)
View
5 django/db/backends/__init__.py
@@ -25,6 +25,11 @@ def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
self.alias = alias
self.use_debug_cursor = None
+ # Transaction related attributes
+ self.transaction_state = []
+ self.savepoint_state = 0
+ self.dirty = None
+
def __eq__(self, other):
return self.alias == other.alias
View
103 django/db/transaction.py
@@ -25,6 +25,7 @@
from django.conf import settings
from django.db import connections, DEFAULT_DB_ALIAS
+
class TransactionManagementError(Exception):
"""
This exception is thrown when something bad happens with transaction
@@ -32,19 +33,6 @@ class TransactionManagementError(Exception):
"""
pass
-# The states are dictionaries of dictionaries of lists. The key to the outer
-# dict is the current thread, and the key to the inner dictionary is the
-# connection alias and the list is handled as a stack of values.
-state = {}
-savepoint_state = {}
-
-# The dirty flag is set by *_unless_managed functions to denote that the
-# code under transaction management has changed things to require a
-# database commit.
-# This is a dictionary mapping thread to a dictionary mapping connection
-# alias to a boolean.
-dirty = {}
-
def enter_transaction_management(managed=True, using=None):
"""
Enters transaction management for a running thread. It must be balanced with
@@ -58,15 +46,14 @@ def enter_transaction_management(managed=True, using=None):
if using is None:
using = DEFAULT_DB_ALIAS
connection = connections[using]
- thread_ident = thread.get_ident()
- if thread_ident in state and state[thread_ident].get(using):
- state[thread_ident][using].append(state[thread_ident][using][-1])
+
+ if connection.transaction_state:
+ connection.transaction_state.append(connection.transaction_state[-1])
else:
- state.setdefault(thread_ident, {})
- state[thread_ident][using] = [settings.TRANSACTIONS_MANAGED]
- if thread_ident not in dirty or using not in dirty[thread_ident]:
- dirty.setdefault(thread_ident, {})
- dirty[thread_ident][using] = False
+ connection.transaction_state.append(settings.TRANSACTIONS_MANAGED)
+
+ if connection.dirty is None:
+ connection.dirty = False
connection._enter_transaction_management(managed)
def leave_transaction_management(using=None):
@@ -78,16 +65,18 @@ def leave_transaction_management(using=None):
if using is None:
using = DEFAULT_DB_ALIAS
connection = connections[using]
+
connection._leave_transaction_management(is_managed(using=using))
- thread_ident = thread.get_ident()
- if thread_ident in state and state[thread_ident].get(using):
- del state[thread_ident][using][-1]
+ if connection.transaction_state:
+ del connection.transaction_state[-1]
else:
- raise TransactionManagementError("This code isn't under transaction management")
- if dirty.get(thread_ident, {}).get(using, False):
+ raise TransactionManagementError("This code isn't under transaction "
+ "management")
+ if connection.dirty:
rollback(using=using)
- raise TransactionManagementError("Transaction managed block ended with pending COMMIT/ROLLBACK")
- dirty[thread_ident][using] = False
+ raise TransactionManagementError("Transaction managed block ended with "
+ "pending COMMIT/ROLLBACK")
+ connection.dirty = False
def is_dirty(using=None):
"""
@@ -96,7 +85,9 @@ def is_dirty(using=None):
"""
if using is None:
using = DEFAULT_DB_ALIAS
- return dirty.get(thread.get_ident(), {}).get(using, False)
+ connection = connections[using]
+
+ return connection.dirty
def set_dirty(using=None):
"""
@@ -106,11 +97,13 @@ def set_dirty(using=None):
"""
if using is None:
using = DEFAULT_DB_ALIAS
- thread_ident = thread.get_ident()
- if thread_ident in dirty and using in dirty[thread_ident]:
- dirty[thread_ident][using] = True
+ connection = connections[using]
+
+ if connection.dirty is not None:
+ connection.dirty = True
else:
- raise TransactionManagementError("This code isn't under transaction management")
+ raise TransactionManagementError("This code isn't under transaction "
+ "management")
def set_clean(using=None):
"""
@@ -120,9 +113,10 @@ def set_clean(using=None):
"""
if using is None:
using = DEFAULT_DB_ALIAS
- thread_ident = thread.get_ident()
- if thread_ident in dirty and using in dirty[thread_ident]:
- dirty[thread_ident][using] = False
+ connection = connections[using]
+
+ if connection.dirty is not None:
+ connection.dirty = False
else:
raise TransactionManagementError("This code isn't under transaction management")
clean_savepoints(using=using)
@@ -130,9 +124,8 @@ def set_clean(using=None):
def clean_savepoints(using=None):
if using is None:
using = DEFAULT_DB_ALIAS
- thread_ident = thread.get_ident()
- if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
- del savepoint_state[thread_ident][using]
+ connection = connections[using]
+ connection.savepoint_state = 0
def is_managed(using=None):
"""
@@ -140,10 +133,9 @@ def is_managed(using=None):
"""
if using is None:
using = DEFAULT_DB_ALIAS
- thread_ident = thread.get_ident()
- if thread_ident in state and using in state[thread_ident]:
- if state[thread_ident][using]:
- return state[thread_ident][using][-1]
+ connection = connections[using]
+ if connection.transaction_state:
+ return connection.transaction_state[-1]
return settings.TRANSACTIONS_MANAGED
def managed(flag=True, using=None):
@@ -156,15 +148,16 @@ def managed(flag=True, using=None):
if using is None:
using = DEFAULT_DB_ALIAS
connection = connections[using]
- thread_ident = thread.get_ident()
- top = state.get(thread_ident, {}).get(using, None)
+
+ top = connection.transaction_state
if top:
top[-1] = flag
if not flag and is_dirty(using=using):
connection._commit()
set_clean(using=using)
else:
- raise TransactionManagementError("This code isn't under transaction management")
+ raise TransactionManagementError("This code isn't under transaction "
+ "management")
def commit_unless_managed(using=None):
"""
@@ -221,13 +214,11 @@ def savepoint(using=None):
using = DEFAULT_DB_ALIAS
connection = connections[using]
thread_ident = thread.get_ident()
- if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
- savepoint_state[thread_ident][using].append(None)
- else:
- savepoint_state.setdefault(thread_ident, {})
- savepoint_state[thread_ident][using] = [None]
+
+ connection.savepoint_state += 1
+
tid = str(thread_ident).replace('-', '')
- sid = "s%s_x%d" % (tid, len(savepoint_state[thread_ident][using]))
+ sid = "s%s_x%d" % (tid, connection.savepoint_state)
connection._savepoint(sid)
return sid
@@ -239,8 +230,8 @@ def savepoint_rollback(sid, using=None):
if using is None:
using = DEFAULT_DB_ALIAS
connection = connections[using]
- thread_ident = thread.get_ident()
- if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
+
+ if connection.savepoint_state:
connection._savepoint_rollback(sid)
def savepoint_commit(sid, using=None):
@@ -251,8 +242,8 @@ def savepoint_commit(sid, using=None):
if using is None:
using = DEFAULT_DB_ALIAS
connection = connections[using]
- thread_ident = thread.get_ident()
- if thread_ident in savepoint_state and using in savepoint_state[thread_ident]:
+
+ if connection.savepoint_state:
connection._savepoint_commit(sid)
##############
View
12 django/utils/thread_support.py
@@ -1,12 +0,0 @@
-"""
-Code used in a couple of places to work with the current thread's environment.
-Current users include i18n and request prefix handling.
-"""
-
-try:
- import threading
- currentThread = threading.currentThread
-except ImportError:
- def currentThread():
- return "no threading"
-
View
31 django/utils/translation/trans_real.py
@@ -7,15 +7,16 @@
import warnings
import gettext as gettext_module
from cStringIO import StringIO
+from threading import local
from django.utils.importlib import import_module
from django.utils.safestring import mark_safe, SafeData
-from django.utils.thread_support import currentThread
+
# Translations are cached in a dictionary for every language+app tuple.
# The active translations are stored by threadid to make them thread local.
_translations = {}
-_active = {}
+_active = local()
# The default translation is based on the settings file.
_default = None
@@ -197,16 +198,15 @@ def activate(language):
"Please use the 'nb' translation instead.",
DeprecationWarning
)
- _active[currentThread()] = translation(language)
+ _active.value = translation(language)
def deactivate():
"""
Deinstalls the currently active translation object so that further _ calls
will resolve against the default translation object, again.
"""
- global _active
- if currentThread() in _active:
- del _active[currentThread()]
+ if hasattr(_active, "value"):
+ del _active.value
def deactivate_all():
"""
@@ -214,11 +214,11 @@ def deactivate_all():
useful when we want delayed translations to appear as the original string
for some reason.
"""
- _active[currentThread()] = gettext_module.NullTranslations()
+ _active.value = gettext_module.NullTranslations()
def get_language():
"""Returns the currently selected language."""
- t = _active.get(currentThread(), None)
+ t = getattr(_active, "value", None)
if t is not None:
try:
return t.to_language()
@@ -246,8 +246,9 @@ def catalog():
This can be used if you need to modify the catalog or want to access the
whole message catalog instead of just translating one string.
"""
- global _default, _active
- t = _active.get(currentThread(), None)
+ global _default
+
+ t = getattr(_active, "value", None)
if t is not None:
return t
if _default is None:
@@ -262,9 +263,10 @@ def do_translate(message, translation_function):
translation object to use. If no current translation is activated, the
message will be run through the default translation object.
"""
+ global _default
+
eol_message = message.replace('\r\n', '\n').replace('\r', '\n')
- global _default, _active
- t = _active.get(currentThread(), None)
+ t = getattr(_active, "value", None)
if t is not None:
result = getattr(t, translation_function)(eol_message)
else:
@@ -300,9 +302,9 @@ def gettext_noop(message):
return message
def do_ntranslate(singular, plural, number, translation_function):
- global _default, _active
+ global _default
- t = _active.get(currentThread(), None)
+ t = getattr(_active, "value", None)
if t is not None:
return getattr(t, translation_function)(singular, plural, number)
if _default is None:
@@ -587,4 +589,3 @@ def get_partial_date_formats():
if month_day_format == 'MONTH_DAY_FORMAT':
month_day_format = settings.MONTH_DAY_FORMAT
return year_month_format, month_day_format
-
View
8 tests/regressiontests/i18n/tests.py
@@ -4,10 +4,12 @@
import os
import sys
import pickle
+from threading import local
from django.conf import settings
from django.template import Template, Context
-from django.utils.formats import get_format, date_format, time_format, localize, localize_input, iter_format_modules
+from django.utils.formats import (get_format, date_format, time_format,
+ localize, localize_input, iter_format_modules)
from django.utils.importlib import import_module
from django.utils.numberformat import format as nformat
from django.utils.safestring import mark_safe, SafeString, SafeUnicode
@@ -61,7 +63,7 @@ def test_pgettext(self):
self.old_locale_paths = settings.LOCALE_PATHS
settings.LOCALE_PATHS += (os.path.join(os.path.dirname(os.path.abspath(__file__)), 'other', 'locale'),)
from django.utils.translation import trans_real
- trans_real._active = {}
+ trans_real._active = local()
trans_real._translations = {}
activate('de')
@@ -649,7 +651,7 @@ def setUp(self):
from django.utils.translation import trans_real
# Okay, this is brutal, but we have no other choice to fully reset
# the translation framework
- trans_real._active = {}
+ trans_real._active = local()
trans_real._translations = {}
activate('de')
Please sign in to comment.
Something went wrong with that request. Please try again.