Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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.