Django

Code

Ticket #1268: current_user.diff

File current_user.diff, 7.8 kB (added by jkocherhans, 3 years ago)

initial attempt

  • django/utils/threading.py

    old new  
     1# Copy of _thread_local.py from python 2.4. Used for python 2.3 support. 
     2"""Thread-local objects 
     3 
     4(Note that this module provides a Python version of thread 
     5 threading.local class.  Depending on the version of Python you're 
     6 using, there may be a faster one available.  You should always import 
     7 the local class from threading.) 
     8 
     9Thread-local objects support the management of thread-local data. 
     10If you have data that you want to be local to a thread, simply create 
     11a thread-local object and use its attributes: 
     12 
     13  >>> mydata = local() 
     14  >>> mydata.number = 42 
     15  >>> mydata.number 
     16  42 
     17 
     18You can also access the local-object's dictionary: 
     19 
     20  >>> mydata.__dict__ 
     21  {'number': 42} 
     22  >>> mydata.__dict__.setdefault('widgets', []) 
     23  [] 
     24  >>> mydata.widgets 
     25  [] 
     26 
     27What's important about thread-local objects is that their data are 
     28local to a thread. If we access the data in a different thread: 
     29 
     30  >>> log = [] 
     31  >>> def f(): 
     32  ...     items = mydata.__dict__.items() 
     33  ...     items.sort() 
     34  ...     log.append(items) 
     35  ...     mydata.number = 11 
     36  ...     log.append(mydata.number) 
     37 
     38  >>> import threading 
     39  >>> thread = threading.Thread(target=f) 
     40  >>> thread.start() 
     41  >>> thread.join() 
     42  >>> log 
     43  [[], 11] 
     44 
     45we get different data.  Furthermore, changes made in the other thread 
     46don't affect data seen in this thread: 
     47 
     48  >>> mydata.number 
     49  42 
     50 
     51Of course, values you get from a local object, including a __dict__ 
     52attribute, are for whatever thread was current at the time the 
     53attribute was read.  For that reason, you generally don't want to save 
     54these values across threads, as they apply only to the thread they 
     55came from. 
     56 
     57You can create custom local objects by subclassing the local class: 
     58 
     59  >>> class MyLocal(local): 
     60  ...     number = 2 
     61  ...     initialized = False 
     62  ...     def __init__(self, **kw): 
     63  ...         if self.initialized: 
     64  ...             raise SystemError('__init__ called too many times') 
     65  ...         self.initialized = True 
     66  ...         self.__dict__.update(kw) 
     67  ...     def squared(self): 
     68  ...         return self.number ** 2 
     69 
     70This can be useful to support default values, methods and 
     71initialization.  Note that if you define an __init__ method, it will be 
     72called each time the local object is used in a separate thread.  This 
     73is necessary to initialize each thread's dictionary. 
     74 
     75Now if we create a local object: 
     76 
     77  >>> mydata = MyLocal(color='red') 
     78 
     79Now we have a default number: 
     80 
     81  >>> mydata.number 
     82  2 
     83 
     84an initial color: 
     85 
     86  >>> mydata.color 
     87  'red' 
     88  >>> del mydata.color 
     89 
     90And a method that operates on the data: 
     91 
     92  >>> mydata.squared() 
     93  4 
     94 
     95As before, we can access the data in a separate thread: 
     96 
     97  >>> log = [] 
     98  >>> thread = threading.Thread(target=f) 
     99  >>> thread.start() 
     100  >>> thread.join() 
     101  >>> log 
     102  [[('color', 'red'), ('initialized', True)], 11] 
     103 
     104without affecting this thread's data: 
     105 
     106  >>> mydata.number 
     107  2 
     108  >>> mydata.color 
     109  Traceback (most recent call last): 
     110  ... 
     111  AttributeError: 'MyLocal' object has no attribute 'color' 
     112 
     113Note that subclasses can define slots, but they are not thread 
     114local. They are shared across threads: 
     115 
     116  >>> class MyLocal(local): 
     117  ...     __slots__ = 'number' 
     118 
     119  >>> mydata = MyLocal() 
     120  >>> mydata.number = 42 
     121  >>> mydata.color = 'red' 
     122 
     123So, the separate thread: 
     124 
     125  >>> thread = threading.Thread(target=f) 
     126  >>> thread.start() 
     127  >>> thread.join() 
     128 
     129affects what we see: 
     130 
     131  >>> mydata.number 
     132  11 
     133 
     134>>> del mydata 
     135""" 
     136 
     137# Threading import is at end 
     138 
     139class _localbase(object): 
     140    __slots__ = '_local__key', '_local__args', '_local__lock' 
     141 
     142    def __new__(cls, *args, **kw): 
     143        self = object.__new__(cls) 
     144        key = '_local__key', 'thread.local.' + str(id(self)) 
     145        object.__setattr__(self, '_local__key', key) 
     146        object.__setattr__(self, '_local__args', (args, kw)) 
     147        object.__setattr__(self, '_local__lock', RLock()) 
     148 
     149        if args or kw and (cls.__init__ is object.__init__): 
     150            raise TypeError("Initialization arguments are not supported") 
     151 
     152        # We need to create the thread dict in anticipation of 
     153        # __init__ being called, to make sire we don't cal it 
     154        # again ourselves. 
     155        dict = object.__getattribute__(self, '__dict__') 
     156        currentThread().__dict__[key] = dict 
     157 
     158        return self 
     159 
     160def _patch(self): 
     161    key = object.__getattribute__(self, '_local__key') 
     162    d = currentThread().__dict__.get(key) 
     163    if d is None: 
     164        d = {} 
     165        currentThread().__dict__[key] = d 
     166        object.__setattr__(self, '__dict__', d) 
     167 
     168        # we have a new instance dict, so call out __init__ if we have 
     169        # one 
     170        cls = type(self) 
     171        if cls.__init__ is not object.__init__: 
     172            args, kw = object.__getattribute__(self, '_local__args') 
     173            cls.__init__(self, *args, **kw) 
     174    else: 
     175        object.__setattr__(self, '__dict__', d) 
     176 
     177class local(_localbase): 
     178 
     179    def __getattribute__(self, name): 
     180        lock = object.__getattribute__(self, '_local__lock') 
     181        lock.acquire() 
     182        try: 
     183            _patch(self) 
     184            return object.__getattribute__(self, name) 
     185        finally: 
     186            lock.release() 
     187 
     188    def __setattr__(self, name, value): 
     189        lock = object.__getattribute__(self, '_local__lock') 
     190        lock.acquire() 
     191        try: 
     192            _patch(self) 
     193            return object.__setattr__(self, name, value) 
     194        finally: 
     195            lock.release() 
     196 
     197    def __delattr__(self, name): 
     198        lock = object.__getattribute__(self, '_local__lock') 
     199        lock.acquire() 
     200        try: 
     201            _patch(self) 
     202            return object.__delattr__(self, name) 
     203        finally: 
     204            lock.release() 
     205 
     206 
     207    def __del__(): 
     208        threading_enumerate = enumerate 
     209        __getattribute__ = object.__getattribute__ 
     210 
     211        def __del__(self): 
     212            key = __getattribute__(self, '_local__key') 
     213 
     214            try: 
     215                threads = list(threading_enumerate()) 
     216            except: 
     217                # if enumerate fails, as it seems to do during 
     218                # shutdown, we'll skip cleanup under the assumption 
     219                # that there is nothing to clean up 
     220                return 
     221 
     222            for thread in threads: 
     223                try: 
     224                    __dict__ = thread.__dict__ 
     225                except AttributeError: 
     226                    # Thread is dying, rest in peace 
     227                    continue 
     228 
     229                if key in __dict__: 
     230                    try: 
     231                        del __dict__[key] 
     232                    except KeyError: 
     233                        pass # didn't have anything in this thread 
     234 
     235        return __del__ 
     236    __del__ = __del__() 
     237 
     238from threading import currentThread, enumerate, RLock 
  • django/contrib/auth/middleware.py

    old new  
     1from django.contrib.auth import set_user 
     2 
     3class AuthMiddleware: 
     4    def process_request(self, request): 
     5        set_user(request.user) 
  • django/contrib/auth/__init__.py

    old new  
     1try: 
     2    # only exists in python 2.4+ 
     3    from threading import local 
     4except ImportError: 
     5    # import copy of _thread_local.py from python 2.4 
     6    from django.utils.threading import local 
     7 
     8thread_locals = local() 
     9 
     10def get_user(): 
     11    return thread_locals.user 
     12 
     13def set_user(user): 
     14    thread_locals.user = user