[Zope3-checkins] SVN: Zope3/trunk/s Implemented a thread-local data proposal

Jim Fulton jim at zope.com
Thu Jul 1 15:05:31 EDT 2004


Log message for revision 26023:
Implemented a thread-local data proposal

Proposed for Python 2.4 on python-dev:
  http://mail.python.org/pipermail/python-dev/2004-June/045785.html    

This mechanism replaces the previous "thread globals"
mechanism.




-=-
Modified: Zope3/trunk/setup.py
===================================================================
--- Zope3/trunk/setup.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/setup.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -243,6 +243,9 @@
     Extension("zope.hookable._zope_hookable",
               ["src/zope/hookable/_zope_hookable.c"]),
 
+    Extension("zope.thread._zope_thread",
+              ["src/zope/thread/_zope_thread.c"]),
+
     Extension("zope.app.container._zope_app_container_contained",
               ["src/zope/app/container/_zope_app_container_contained.c"],
               include_dirs = ["src/persistent",

Modified: Zope3/trunk/src/zope/app/component/hooks.py
===================================================================
--- Zope3/trunk/src/zope/app/component/hooks.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/app/component/hooks.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -16,7 +16,6 @@
 $Id$
 """
 
-import warnings
 from zope.component import getService, getAdapter
 from zope.component.interfaces import IServiceService
 from zope.app.site.interfaces import ISite
@@ -29,20 +28,26 @@
 from zope.app.location import locate
 from zope.component.servicenames import Presentation
 from zope.interface import Interface
-from zope.thread import thread_globals
+import warnings
+import zope.thread
 
+siteinfo = zope.thread.local()
+
 def setSite(site=None):
     if site is None:
-        services = None
+        siteinfo.services = None
     else:
-        services = trustedRemoveSecurityProxy(site.getSiteManager())
+        siteinfo.services = trustedRemoveSecurityProxy(site.getSiteManager())
 
-    thread_globals().services = services
-
 def getSite():
-    services = thread_globals().services
+    try:
+        services = siteinfo.services
+    except AttributeError:
+        services = siteinfo.services = None
+        
     if services is None:
         return None
+
     return services.__parent__
     
 
@@ -50,15 +55,15 @@
 
     if context is None:
         try:
-            services = thread_globals().services
+            services = siteinfo.services
         except AttributeError:
-            thread_globals().services = services = None
-            
+            services = siteinfo.services = None
+
         if services is None:
             return serviceManager
-        else:
-            return services
 
+        return services
+
     try:
         # This try-except is just backward compatibility really
         return trustedRemoveSecurityProxy(getAdapter(context, IServiceService))

Modified: Zope3/trunk/src/zope/security/management.py
===================================================================
--- Zope3/trunk/src/zope/security/management.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/security/management.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -25,8 +25,10 @@
 from zope.security.interfaces import ISecurityManagement
 from zope.security.interfaces import IInteractionManagement
 from zope.testing.cleanup import addCleanUp
-from zope.thread import thread_globals
+import zope.thread
 
+thread_local = zope.thread.local()
+
 moduleProvides(ISecurityManagement, IInteractionManagement)
 
 
@@ -63,32 +65,28 @@
 #   IInteractionManagement implementation
 #
 
-def queryInteraction(_thread=None):
+def queryInteraction():
     """Get the current interaction."""
-    return thread_globals(_thread).interaction
+    return getattr(thread_local, 'interaction', None)
 
-def newInteraction(participation=None, _thread=None, _policy=None):
+def newInteraction(participation=None, _policy=None):
     """Start a new interaction."""
-    if queryInteraction(_thread) is not None:
-        stack = queryInteraction(_thread)._newInteraction_called_from
+    if queryInteraction() is not None:
+        stack = queryInteraction()._newInteraction_called_from
         raise AssertionError("newInteraction called"
                              " while another interaction is active:\n%s"
                              % "".join(traceback.format_list(stack)))
     interaction = getSecurityPolicy().createInteraction(participation)
     interaction._newInteraction_called_from = traceback.extract_stack()
-    thread_globals(_thread).interaction = interaction
+    thread_local.interaction = interaction
 
-def endInteraction(_thread=None):
+def endInteraction():
     """End the current interaction."""
-    thread_globals(_thread).interaction = None
+    thread_local.interaction = None
 
+addCleanUp(endInteraction)
 
-def _cleanUp():
-    thread_globals().interaction = None
 
-addCleanUp(_cleanUp)
-
-
 # circular imports are not fun
 
 from zope.security.simplepolicies import ParanoidSecurityPolicy

Modified: Zope3/trunk/src/zope/security/tests/test_management.py
===================================================================
--- Zope3/trunk/src/zope/security/tests/test_management.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/security/tests/test_management.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -41,52 +41,25 @@
         setSecurityPolicy(policy)
         self.assert_(getSecurityPolicy() is policy)
 
-    def test_queryInteraction(self):
-        # XXX this test is a bit obfuscated
+    def test_query_new_end_Interaction(self):
         from zope.security.management import queryInteraction
+        self.assertEquals(queryInteraction(), None)
 
-        marker = object()
-        class ThreadVars:
-            interaction = marker
-        class ThreadStub:
-            __zope3_thread_globals__ = ThreadVars()
-
-        self.assert_(queryInteraction(_thread=ThreadStub()) is marker)
-
-    def test_newInteraction(self):
-        # XXX this test is a bit obfuscated
         from zope.security.management import newInteraction
 
-        class ThreadVars:
-            interaction = None
-        class ThreadStub:
-            __zope3_thread_globals__ = ThreadVars()
-
         rq = None
-        thread = ThreadStub()
-        newInteraction(rq, _thread=thread)
-        self.assert_(thread.__zope3_thread_globals__.interaction is not None)
+        newInteraction(rq)
 
-        self.assertRaises(AssertionError, newInteraction, rq, _thread=thread)
+        self.assert_(queryInteraction() is not None)
+        self.assertRaises(AssertionError, newInteraction, rq)
 
-    def test_endInteraction(self):
-        # XXX this test is a bit obfuscated
         from zope.security.management import endInteraction
 
-        marker = object()
-        class ThreadVars:
-            interaction = marker
-        class ThreadStub:
-            __zope3_thread_globals__ = ThreadVars()
+        endInteraction()
+        self.assertEquals(queryInteraction(), None)
+        endInteraction()
+        self.assertEquals(queryInteraction(), None)
 
-        thread = ThreadStub()
-        endInteraction(_thread=thread)
-        self.assert_(thread.__zope3_thread_globals__.interaction is None)
-
-        # again
-        endInteraction(_thread=thread)
-        self.assert_(thread.__zope3_thread_globals__.interaction is None)
-
     def test_checkPermission(self):
         from zope.security import checkPermission
         from zope.security.management import setSecurityPolicy

Modified: Zope3/trunk/src/zope/thread/DEPENDENCIES.cfg
===================================================================
--- Zope3/trunk/src/zope/thread/DEPENDENCIES.cfg	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/thread/DEPENDENCIES.cfg	2004-07-01 19:05:30 UTC (rev 26023)
@@ -1 +0,0 @@
-zope.interface

Modified: Zope3/trunk/src/zope/thread/__init__.py
===================================================================
--- Zope3/trunk/src/zope/thread/__init__.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/thread/__init__.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -11,33 +11,216 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""zope.thread
+"""Thread-local objects
 
-Implements thread global variables.
+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 it's attributes:
+
+  >>> import zope.thread
+  >>> mydata = zope.thread.local()
+  >>> mydata.__class__.__name__
+  '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(zope.thread.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 effecting this threads 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(zope.thread.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
 """
 
-import threading
-from zope.interface import moduleProvides, implements
-from zope.thread.interfaces import IZopeThreadAPI
-from zope.thread.interfaces import IInteractionThreadGlobal, ISiteThreadGlobal
+try:
+    import _zope_thread
+except ImportError:
+    from threading import currentThread, enumerate, RLock
 
-__metaclass__ = type
+    class _localbase(object):
+        __slots__ = '_local__key', '_local__args', '_local__lock'
 
-moduleProvides(IZopeThreadAPI)
+        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")
 
-def thread_globals(thread=None):
-    """See IZopeThreadAPI."""
-    if thread is None:
-        thread = threading.currentThread()
-    if not hasattr(thread, '__zope3_thread_globals__'):
-        thread.__zope3_thread_globals__ = ThreadGlobals()
-    return thread.__zope3_thread_globals__
+            # 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
 
-class ThreadGlobals:
-    implements(IInteractionThreadGlobal, ISiteThreadGlobal)
+    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)
 
-    interaction = None
-    site = None
+            # 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__(self, enumerate=enumerate):
+            key = object.__getattribute__(self, '_local__key')
+            for thread in enumerate():
+                if key in thread.__dict__:
+                    del thread.__dict__[key]
+
+else:
+    local = _zope_thread.local
+    del _zope_thread

Added: Zope3/trunk/src/zope/thread/_zope_thread.c
===================================================================
--- Zope3/trunk/src/zope/thread/_zope_thread.c	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/thread/_zope_thread.c	2004-07-01 19:05:30 UTC (rev 26023)
@@ -0,0 +1,307 @@
+/*
+
+ Copyright (c) 2003 Zope Corporation and Contributors.
+ All Rights Reserved.
+
+ This software is subject to the provisions of the Zope Public License,
+ Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+ THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+ WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+ FOR A PARTICULAR PURPOSE.
+
+*/
+
+#include "Python.h"
+#include "structmember.h"
+
+#define CLEAR(O) if (O) {PyObject *t = O; O = 0; Py_DECREF(t); }
+
+typedef struct {
+    PyObject_HEAD
+    PyObject *key;
+    PyObject *args;
+    PyObject *kw;
+    PyObject *dict;
+} localobject;
+
+static PyTypeObject localtype;
+
+static PyObject *
+local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
+{
+    	localobject *self;
+        PyObject *tdict;
+
+        if (type->tp_init == PyBaseObject_Type.tp_init
+            && ((args && PyObject_IsTrue(args))
+                ||
+                (kw && PyObject_IsTrue(kw))
+                )
+            ) {
+          	PyErr_SetString(PyExc_TypeError,
+                          "Initialization arguments are not supported");
+                return NULL;
+        }
+
+    	self = (localobject *)type->tp_alloc(type, 0);
+        if (self == NULL)
+          return NULL;
+
+        Py_XINCREF(args);
+        self->args = args;
+        Py_XINCREF(kw);
+        self->kw = kw;
+        self->dict = NULL;      /* making sure */
+        self->key = PyString_FromFormat("thread.local.%p", self);
+        if (self->key == NULL) 
+                goto err;
+
+        self->dict = PyDict_New();
+        if (self->dict == NULL)
+                goto err;
+
+        tdict = PyThreadState_GetDict();
+        if (tdict == NULL) {
+                PyErr_SetString(PyExc_SystemError,
+                                "Couldn't get thread-state dictionary");
+                goto err;
+        }
+
+        if (PyDict_SetItem(tdict, self->key, self->dict) < 0)
+                goto err;
+       
+    	return (PyObject *)self;
+
+ err:
+        Py_DECREF(self);
+        return NULL;
+}
+
+static int
+local_traverse(localobject *self, visitproc visit, void *arg)
+{
+	if (self->args != NULL && visit(self->args, arg) < 0)
+		return -1;
+	if (self->kw != NULL && visit(self->kw, arg) < 0)
+		return -1;
+	if (self->dict != NULL && visit(self->dict, arg) < 0)
+		return -1;
+	return 0;
+}
+
+static int
+local_clear(localobject *self)
+{
+  	CLEAR(self->key);
+        CLEAR(self->args);
+        CLEAR(self->kw);
+        CLEAR(self->dict);
+        return 0;
+}
+
+static void
+local_dealloc(localobject *self)
+{
+        PyThreadState *tstate;
+        if (self->key
+            && (tstate = PyThreadState_Get())
+            && tstate->interp) {
+                for(tstate = PyInterpreterState_ThreadHead(tstate->interp);
+                    tstate;
+                    tstate = PyThreadState_Next(tstate)
+                    ) 
+                        if (tstate->dict &&
+                            PyDict_GetItem(tstate->dict, self->key))
+                                PyDict_DelItem(tstate->dict, self->key);
+        }
+
+  	local_clear(self);
+        self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *
+_ldict(localobject *self)
+{
+	PyObject *tdict, *ldict;
+
+	tdict = PyThreadState_GetDict();
+        if (tdict == NULL) {
+        	PyErr_SetString(PyExc_SystemError,
+                                "Couldn't get thread-state dictionary");
+                return NULL;
+        }
+
+        ldict = PyDict_GetItem(tdict, self->key);
+        if (ldict == NULL) {
+        	ldict = PyDict_New(); /* we own ldict */
+
+                if (ldict == NULL)
+                	return NULL;
+                else {
+                        int i = PyDict_SetItem(tdict, self->key, ldict);
+                        Py_DECREF(ldict); /* now ldict is borowed */
+                        if (i < 0) 
+                                return NULL;
+                }
+
+                CLEAR(self->dict);
+                Py_INCREF(ldict);
+                self->dict = ldict; /* still borrowed */
+
+                if (self->ob_type->tp_init != PyBaseObject_Type.tp_init &&
+                    self->ob_type->tp_init((PyObject*)self, 
+                                           self->args, self->kw) < 0
+                    ) {
+                        /* we need to get rid of ldict from thread so
+                           we create a new one the next time we do an attr
+                           acces */
+                        PyDict_DelItem(tdict, self->key);
+                        return NULL;
+                }
+                
+        }
+        else if (self->dict != ldict) {
+                CLEAR(self->dict);
+                Py_INCREF(ldict);
+                self->dict = ldict;
+        }
+
+  return ldict;
+}
+
+static PyObject *
+local_getattro(localobject *self, PyObject *name)
+{
+	PyObject *ldict, *value;
+
+        ldict = _ldict(self);
+        if (ldict == NULL) 
+        	return NULL;
+
+        if (self->ob_type != &localtype)
+                /* use generic lookup for subtypes */
+                return PyObject_GenericGetAttr((PyObject *)self, name);
+
+        /* Optimization: just look in dict ourselves */
+        value = PyDict_GetItem(ldict, name);
+        if (value == NULL) 
+                /* Fall back on generic to get __class__ and __dict__ */
+                return PyObject_GenericGetAttr((PyObject *)self, name);
+
+        Py_INCREF(value);
+        return value;
+}
+
+static int
+local_setattro(localobject *self, PyObject *name, PyObject *v)
+{
+	PyObject *ldict;
+        
+        ldict = _ldict(self);
+        if (ldict == NULL) 
+          	return -1;
+
+        return PyObject_GenericSetAttr((PyObject *)self, name, v);
+}
+
+static PyObject *
+local_getdict(localobject *self, void *closure)
+{
+        if (self->dict == NULL) {
+                PyErr_SetString(PyExc_AttributeError, "__dict__");
+                return NULL;
+        }
+
+    	Py_INCREF(self->dict);
+        return self->dict;
+}
+
+static PyGetSetDef local_getset[] = {
+    {"__dict__", 
+     (getter)local_getdict, (setter)0,
+     "Local-data dictionary",
+     NULL},
+    {NULL}  /* Sentinel */
+};
+
+static PyTypeObject localtype = {
+	PyObject_HEAD_INIT(NULL)
+	/* ob_size           */ 0,
+	/* tp_name           */ "zope.thread.local",
+	/* tp_basicsize      */ sizeof(localobject),
+	/* tp_itemsize       */ 0,
+	/* tp_dealloc        */ (destructor)local_dealloc,
+	/* tp_print          */ (printfunc)0,
+	/* tp_getattr        */ (getattrfunc)0,
+	/* tp_setattr        */ (setattrfunc)0,
+	/* tp_compare        */ (cmpfunc)0,
+	/* tp_repr           */ (reprfunc)0,
+	/* tp_as_number      */ 0,
+	/* tp_as_sequence    */ 0,
+	/* tp_as_mapping     */ 0,
+	/* tp_hash           */ (hashfunc)0,
+	/* tp_call           */ (ternaryfunc)0,
+	/* tp_str            */ (reprfunc)0,
+        /* tp_getattro       */ (getattrofunc)local_getattro,
+        /* tp_setattro       */ (setattrofunc)local_setattro,
+        /* tp_as_buffer      */ 0,
+        /* tp_flags          */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+	/* tp_doc            */ "Thread-local data",
+        /* tp_traverse       */ (traverseproc)local_traverse,
+        /* tp_clear          */ (inquiry)local_clear,
+        /* tp_richcompare    */ (richcmpfunc)0,
+        /* tp_weaklistoffset */ (long)0,
+        /* tp_iter           */ (getiterfunc)0,
+        /* tp_iternext       */ (iternextfunc)0,
+        /* tp_methods        */ 0,
+        /* tp_members        */ 0,
+        /* tp_getset         */ local_getset,
+        /* tp_base           */ 0,
+        /* tp_dict           */ 0, /* internal use */
+        /* tp_descr_get      */ (descrgetfunc)0,
+        /* tp_descr_set      */ (descrsetfunc)0,
+        /* tp_dictoffset     */ offsetof(localobject, dict),
+        /* tp_init           */ (initproc)0,
+        /* tp_alloc          */ (allocfunc)0,
+        /* tp_new            */ (newfunc)local_new,
+	/* tp_free           */ 0, /* Low-level free-mem routine */
+	/* tp_is_gc          */ (inquiry)0, /* For PyObject_IS_GC */
+};
+
+/* End of code for local objects */
+/* -------------------------------------------------------- */
+
+
+/* List of methods defined in the module */
+
+static struct PyMethodDef _zope_thread_methods[] = {
+
+	{NULL,	 (PyCFunction)NULL, 0, NULL}		/* sentinel */
+};
+
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_zope_thread(void)
+{
+	PyObject *m;
+        
+        /* Initialize types: */
+        if (PyType_Ready(&localtype) < 0)
+        	return;
+        
+	/* Create the module and add the functions */
+	m = Py_InitModule3("_zope_thread", _zope_thread_methods,
+                           "zope.thread C implementation");
+
+        if (m == NULL)
+        	return;
+      
+        /* Add types: */
+        if (PyModule_AddObject(m, "local", (PyObject *)&localtype) < 0)
+        	return;
+   }
+


Property changes on: Zope3/trunk/src/zope/thread/_zope_thread.c
___________________________________________________________________
Name: svn:eol-style
   + native

Deleted: Zope3/trunk/src/zope/thread/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/thread/interfaces.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/thread/interfaces.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -1,38 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2002 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Interfaces for zope.thread.
-
-$Id$
-"""
-
-from zope.interface import Interface, Attribute
-
-
-class IZopeThreadAPI(Interface):
-
-    def thread_globals(thread=None):
-        """Return the thread globals instance for the given thread.
-
-        If thread is None, returns the globals for the current thread.
-        """
-
-
-class IInteractionThreadGlobal(Interface):
-
-    interaction = Attribute("""IInteraction for the current thread.""")
-
-
-class ISiteThreadGlobal(Interface):
-
-    site = Attribute("""Site for the current thread.""")

Deleted: Zope3/trunk/src/zope/thread/tests/__init__.py
===================================================================
--- Zope3/trunk/src/zope/thread/tests/__init__.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/thread/tests/__init__.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -1,2 +0,0 @@
-#
-# This file is necessary to make this directory a package.

Deleted: Zope3/trunk/src/zope/thread/tests/test_thread.py
===================================================================
--- Zope3/trunk/src/zope/thread/tests/test_thread.py	2004-07-01 19:02:08 UTC (rev 26022)
+++ Zope3/trunk/src/zope/thread/tests/test_thread.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -1,56 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Unit tests for zope.thread.
-
-$Id$
-"""
-
-import unittest
-from zope.interface.verify import verifyObject
-
-
-class ThreadStub:
-    pass
-
-
-class TestThread(unittest.TestCase):
-
-    def test_ThreadGlobals(self):
-        from zope.thread import ThreadGlobals
-        from zope.thread.interfaces import IInteractionThreadGlobal
-        from zope.thread.interfaces import ISiteThreadGlobal
-        globals = ThreadGlobals()
-        verifyObject(IInteractionThreadGlobal, globals)
-        verifyObject(ISiteThreadGlobal, globals)
-
-    def test_thread_globals(self):
-        from zope.thread import thread_globals
-        from zope.thread.interfaces import IInteractionThreadGlobal
-        fake_thread = ThreadStub()
-        another_thread = ThreadStub()
-        globals = thread_globals(fake_thread)
-        verifyObject(IInteractionThreadGlobal, globals)
-        self.assert_(thread_globals(fake_thread) is globals)
-        self.assert_(thread_globals(another_thread) is not globals)
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestThread))
-    return suite
-
-
-if __name__ == '__main__':
-    unittest.main()
-

Copied: Zope3/trunk/src/zope/thread/tests.py (from rev 25993, Zope3/trunk/src/zope/thread/tests/test_thread.py)
===================================================================
--- Zope3/trunk/src/zope/thread/tests/test_thread.py	2004-06-26 19:15:34 UTC (rev 25993)
+++ Zope3/trunk/src/zope/thread/tests.py	2004-07-01 19:05:30 UTC (rev 26023)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Unit tests for zope.thread.
+
+$Id$
+"""
+
+def test_suite():
+    import unittest
+    from doctest import DocTestSuite
+    suite = unittest.TestSuite()
+    suite.addTest(DocTestSuite('zope.thread'))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+



More information about the Zope3-Checkins mailing list