[Erp5-report] r15061 - /erp5/trunk/utils/oood/

nobody at svn.erp5.org nobody at svn.erp5.org
Fri Jun 29 10:39:41 CEST 2007


Author: kazuhiko
Date: Fri Jun 29 10:39:40 2007
New Revision: 15061

URL: http://svn.erp5.org?rev=15061&view=rev
Log:
revise for python-2.3 compatibility.

Added:
    erp5/trunk/utils/oood/_threading_local.py
Modified:
    erp5/trunk/utils/oood/dispatcher.py
    erp5/trunk/utils/oood/logger.py
    erp5/trunk/utils/oood/oood_common.py

Added: erp5/trunk/utils/oood/_threading_local.py
URL: http://svn.erp5.org/erp5/trunk/utils/oood/_threading_local.py?rev=15061&view=auto
==============================================================================
--- erp5/trunk/utils/oood/_threading_local.py (added)
+++ erp5/trunk/utils/oood/_threading_local.py Fri Jun 29 10:39:40 2007
@@ -1,0 +1,237 @@
+"""Thread-local objects
+
+(Note that this module provides a Python version of thread
+ threading.local class.  Depending on the version of Python you're
+ using, there may be a faster one available.  You should always import
+ the local class from threading.)
+
+Thread-local objects support the management of thread-local data.
+If you have data that you want to be local to a thread, simply create
+a thread-local object and use its attributes:
+
+  >>> mydata = local()
+  >>> mydata.number = 42
+  >>> mydata.number
+  42
+
+You can also access the local-object's dictionary:
+
+  >>> mydata.__dict__
+  {'number': 42}
+  >>> mydata.__dict__.setdefault('widgets', [])
+  []
+  >>> mydata.widgets
+  []
+
+What's important about thread-local objects is that their data are
+local to a thread. If we access the data in a different thread:
+
+  >>> log = []
+  >>> def f():
+  ...     items = mydata.__dict__.items()
+  ...     items.sort()
+  ...     log.append(items)
+  ...     mydata.number = 11
+  ...     log.append(mydata.number)
+
+  >>> import threading
+  >>> thread = threading.Thread(target=f)
+  >>> thread.start()
+  >>> thread.join()
+  >>> log
+  [[], 11]
+
+we get different data.  Furthermore, changes made in the other thread
+don't affect data seen in this thread:
+
+  >>> mydata.number
+  42
+
+Of course, values you get from a local object, including a __dict__
+attribute, are for whatever thread was current at the time the
+attribute was read.  For that reason, you generally don't want to save
+these values across threads, as they apply only to the thread they
+came from.
+
+You can create custom local objects by subclassing the local class:
+
+  >>> class MyLocal(local):
+  ...     number = 2
+  ...     initialized = False
+  ...     def __init__(self, **kw):
+  ...         if self.initialized:
+  ...             raise SystemError('__init__ called too many times')
+  ...         self.initialized = True
+  ...         self.__dict__.update(kw)
+  ...     def squared(self):
+  ...         return self.number ** 2
+
+This can be useful to support default values, methods and
+initialization.  Note that if you define an __init__ method, it will be
+called each time the local object is used in a separate thread.  This
+is necessary to initialize each thread's dictionary.
+
+Now if we create a local object:
+
+  >>> mydata = MyLocal(color='red')
+
+Now we have a default number:
+
+  >>> mydata.number
+  2
+
+an initial color:
+
+  >>> mydata.color
+  'red'
+  >>> del mydata.color
+
+And a method that operates on the data:
+
+  >>> mydata.squared()
+  4
+
+As before, we can access the data in a separate thread:
+
+  >>> log = []
+  >>> thread = threading.Thread(target=f)
+  >>> thread.start()
+  >>> thread.join()
+  >>> log
+  [[('color', 'red'), ('initialized', True)], 11]
+
+without affecting this thread's data:
+
+  >>> mydata.number
+  2
+  >>> mydata.color
+  Traceback (most recent call last):
+  ...
+  AttributeError: 'MyLocal' object has no attribute 'color'
+
+Note that subclasses can define slots, but they are not thread
+local. They are shared across threads:
+
+  >>> class MyLocal(local):
+  ...     __slots__ = 'number'
+
+  >>> mydata = MyLocal()
+  >>> mydata.number = 42
+  >>> mydata.color = 'red'
+
+So, the separate thread:
+
+  >>> thread = threading.Thread(target=f)
+  >>> thread.start()
+  >>> thread.join()
+
+affects what we see:
+
+  >>> mydata.number
+  11
+
+>>> del mydata
+"""
+
+# Threading import is at end
+
+class _localbase(object):
+    __slots__ = '_local__key', '_local__args', '_local__lock'
+
+    def __new__(cls, *args, **kw):
+        self = object.__new__(cls)
+        key = '_local__key', 'thread.local.' + str(id(self))
+        object.__setattr__(self, '_local__key', key)
+        object.__setattr__(self, '_local__args', (args, kw))
+        object.__setattr__(self, '_local__lock', RLock())
+
+        if args or kw and (cls.__init__ is object.__init__):
+            raise TypeError("Initialization arguments are not supported")
+
+        # We need to create the thread dict in anticipation of
+        # __init__ being called, to make sire we don't cal it
+        # again ourselves.
+        dict = object.__getattribute__(self, '__dict__')
+        currentThread().__dict__[key] = dict
+
+        return self
+
+def _patch(self):
+    key = object.__getattribute__(self, '_local__key')
+    d = currentThread().__dict__.get(key)
+    if d is None:
+        d = {}
+        currentThread().__dict__[key] = d
+        object.__setattr__(self, '__dict__', d)
+
+        # we have a new instance dict, so call out __init__ if we have
+        # one
+        cls = type(self)
+        if cls.__init__ is not object.__init__:
+            args, kw = object.__getattribute__(self, '_local__args')
+            cls.__init__(self, *args, **kw)
+    else:
+        object.__setattr__(self, '__dict__', d)
+
+class local(_localbase):
+
+    def __getattribute__(self, name):
+        lock = object.__getattribute__(self, '_local__lock')
+        lock.acquire()
+        try:
+            _patch(self)
+            return object.__getattribute__(self, name)
+        finally:
+            lock.release()
+
+    def __setattr__(self, name, value):
+        lock = object.__getattribute__(self, '_local__lock')
+        lock.acquire()
+        try:
+            _patch(self)
+            return object.__setattr__(self, name, value)
+        finally:
+            lock.release()
+
+    def __delattr__(self, name):
+        lock = object.__getattribute__(self, '_local__lock')
+        lock.acquire()
+        try:
+            _patch(self)
+            return object.__delattr__(self, name)
+        finally:
+            lock.release()
+
+
+    def __del__():
+        threading_enumerate = enumerate
+        __getattribute__ = object.__getattribute__
+
+        def __del__(self):
+            key = __getattribute__(self, '_local__key')
+
+            try:
+                threads = list(threading_enumerate())
+            except:
+                # if enumerate fails, as it seems to do during
+                # shutdown, we'll skip cleanup under the assumption
+                # that there is nothing to clean up
+                return
+
+            for thread in threads:
+                try:
+                    __dict__ = thread.__dict__
+                except AttributeError:
+                    # Thread is dying, rest in peace
+                    continue
+
+                if key in __dict__:
+                    try:
+                        del __dict__[key]
+                    except KeyError:
+                        pass # didn't have anything in this thread
+
+        return __del__
+    __del__ = __del__()
+
+from threading import currentThread, enumerate, RLock

Modified: erp5/trunk/utils/oood/dispatcher.py
URL: http://svn.erp5.org/erp5/trunk/utils/oood/dispatcher.py?rev=15061&r1=15060&r2=15061&view=diff
==============================================================================
--- erp5/trunk/utils/oood/dispatcher.py (original)
+++ erp5/trunk/utils/oood/dispatcher.py Fri Jun 29 10:39:40 2007
@@ -43,6 +43,13 @@
 import time
 import traceback
 import zipfile
+import shutil
+
+# for python-2.3
+if getattr(threading, 'local', None) is None:
+  from _threading_local import local
+  threading.local = local
+
 
 from SimpleXMLRPCServer import *
 from SocketServer import ThreadingMixIn
@@ -446,7 +453,10 @@
     pth = os.path.join(config.oood_home, self.tmp_dir_name, os.path.basename(filename))
     for f in glob.glob('%s/*'%self.tmp_dir_name):
       if not config.debug_mode:
-        os.remove(f)
+        if os.path.isdir(f):
+          shutil.rmtree(f)
+        else:
+          os.remove(f)
       any = True
     #if not any:
       #Log.warning('no file beginning with %s' % filename)

Modified: erp5/trunk/utils/oood/logger.py
URL: http://svn.erp5.org/erp5/trunk/utils/oood/logger.py?rev=15061&r1=15060&r2=15061&view=diff
==============================================================================
--- erp5/trunk/utils/oood/logger.py (original)
+++ erp5/trunk/utils/oood/logger.py Fri Jun 29 10:39:40 2007
@@ -36,20 +36,21 @@
 import lib
 
 
-logging.basicConfig(level=config.log_level,
-        format='%(asctime)s %(levelname)-8s %(message)s',
-        datefmt='%Y/%m/%d %H:%M:%S',
-        filename=config.log_file,
-        filemode='a')
-
-
 class mylog(object):
 
     funcs = ('debug', 'info', 'warning', 'error', 'critical', 'exception')
 
     def __init__(self):
+	self.logger = logging.getLogger('oood')
+	handler = logging.FileHandler(config.log_file)
+	formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s',
+                                      '%Y/%m/%d %H:%M:%S')
+	handler.setFormatter(formatter)
+	self.logger.addHandler(handler) 
+	self.logger.setLevel(config.log_level)
+
         def mkFunc(f):
-            logfunc = getattr(logging, f)
+            logfunc = getattr(self.logger, f)
             def _func(msg, *a, **kw):
                 thread_name = threading.currentThread().getName()
                 msg = '<%s> %s' %(thread_name, msg)

Modified: erp5/trunk/utils/oood/oood_common.py
URL: http://svn.erp5.org/erp5/trunk/utils/oood/oood_common.py?rev=15061&r1=15060&r2=15061&view=diff
==============================================================================
--- erp5/trunk/utils/oood/oood_common.py (original)
+++ erp5/trunk/utils/oood/oood_common.py Fri Jun 29 10:39:40 2007
@@ -101,35 +101,35 @@
       return func(self, *args, **kwargs)
     return wrappedFunc
 
-  @isCodeValid
   def isOk(self):
     """
       Check if the response is "OK".
     """
     return self.code == 200
-
-  @isCodeValid
+  isOk = isCodeValid(isOk)
+
   def isError(self):
     """
       Check if the response signals an error.
     """
     return self.code != 200
-
-  @isCodeValid
+  isError = isCodeValid(isError)
+
   def isClientError(self):
     """
       Check if the error was caused by client, or was it an
       internal problem of the server.
     """
     return self.code > 400 and self.code < 500
-
-  @isCodeValid
+  isClientError = isCodeValid(isClientError)
+
   def isServerError(self):
     """
       Check if the error was caused by client, or was it an
       internal problem of the server.
     """
     return self.code > 500
+  isServerError = isCodeValid(isServerError)
 
 def responseFactory(response):
   """




More information about the Erp5-report mailing list