[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