Some new blood and some updated versions.
diff --git a/Lib/dos-8x3/exceptio.py b/Lib/dos-8x3/exceptio.py
index 28711df..9c733ce 100644
--- a/Lib/dos-8x3/exceptio.py
+++ b/Lib/dos-8x3/exceptio.py
@@ -1,19 +1,23 @@
 """Class based built-in exception hierarchy.
 
-This is a new feature whereby all the standard built-in exceptions,
-traditionally string objects, are replaced with classes.  This gives
-Python's exception handling mechanism a more object-oriented feel.
+New with Python 1.5, all standard built-in exceptions are now class objects by
+default.  This gives Python's exception handling mechanism a more
+object-oriented feel.  Traditionally they were string objects.  Python will
+fallback to string based exceptions if the interpreter is invoked with the -X
+option, or if some failure occurs during class exception initialization (in
+this case a warning will be printed).
 
-Most existing code should continue to work with class based
-exceptions.  Some tricky uses of IOError may break, but the most
-common uses should work.
+Most existing code should continue to work with class based exceptions.  Some
+tricky uses of IOError may break, but the most common uses should work.
 
-To disable this feature, start the Python executable with the -X option.
+Here is a rundown of the class hierarchy.  You can change this by editing this
+file, but it isn't recommended.  The class names described here are expected
+to be found by the bltinmodule.c file.
 
-Here is a rundown of the class hierarchy.  You can change this by
-editing this file, but it isn't recommended.  The classes with a `*'
-are new with this feature.  They are defined as tuples containing the
-derived exceptions when string-based exceptions are used.
+The classes with a `*' are new as of Python 1.5.  They are defined as tuples
+containing the derived exceptions when string-based exceptions are used.  If
+you define your own class based exceptions, they should be derived from
+Exception.
 
 Exception(*)
  |
@@ -22,7 +26,11 @@
       +-- SystemExit
       +-- KeyboardInterrupt
       +-- ImportError
-      +-- IOError
+      +-- EnvironmentError(*)
+      |    |
+      |    +-- IOError
+      |    +-- OSError(*)
+      |
       +-- EOFError
       +-- RuntimeError
       +-- NameError
diff --git a/Lib/dos-8x3/posixpat.py b/Lib/dos-8x3/posixpat.py
index a5c0de2..fb3b6a6 100755
--- a/Lib/dos-8x3/posixpat.py
+++ b/Lib/dos-8x3/posixpat.py
@@ -354,6 +354,8 @@
     while i < len(comps):
         if comps[i] == '.':
             del comps[i]
+            while i < len(comps) and comps[i] == '':
+                del comps[i]
         elif comps[i] == '..' and i > 0 and comps[i-1] not in ('', '..'):
             del comps[i-1:i+1]
             i = i-1
diff --git a/Lib/dos-8x3/py_compi.py b/Lib/dos-8x3/py_compi.py
index 98b3b21..a6d03d7 100755
--- a/Lib/dos-8x3/py_compi.py
+++ b/Lib/dos-8x3/py_compi.py
@@ -51,7 +51,15 @@
     f.close()
     if codestring and codestring[-1] != '\n':
         codestring = codestring + '\n'
-    codeobject = __builtin__.compile(codestring, dfile or file, 'exec')
+    try:
+        codeobject = __builtin__.compile(codestring, dfile or file, 'exec')
+    except SyntaxError, detail:
+        import traceback, sys, string
+        lines = traceback.format_exception_only(SyntaxError, detail)
+        for line in lines:
+            sys.stderr.write(string.replace(line, 'File "<string>"',
+                                            'File "%s"' % (dfile or file)))
+        return
     if not cfile:
         cfile = file + (__debug__ and 'c' or 'o')
     fc = open(cfile, 'wb')
diff --git a/Lib/dos-8x3/stringio.py b/Lib/dos-8x3/stringio.py
index dba38e4..fc195b9 100755
--- a/Lib/dos-8x3/stringio.py
+++ b/Lib/dos-8x3/stringio.py
@@ -41,8 +41,12 @@
 			self.closed = 1
 			del self.buf, self.pos
 	def isatty(self):
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 		return 0
 	def seek(self, pos, mode = 0):
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 		if self.buflist:
 			self.buf = self.buf + string.joinfields(self.buflist, '')
 			self.buflist = []
@@ -52,8 +56,12 @@
 			pos = pos + self.len
 		self.pos = max(0, pos)
 	def tell(self):
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 		return self.pos
 	def read(self, n = -1):
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 		if self.buflist:
 			self.buf = self.buf + string.joinfields(self.buflist, '')
 			self.buflist = []
@@ -65,6 +73,8 @@
 		self.pos = newpos
 		return r
 	def readline(self, length=None):
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 		if self.buflist:
 			self.buf = self.buf + string.joinfields(self.buflist, '')
 			self.buflist = []
@@ -87,6 +97,8 @@
 			line = self.readline()
 		return lines
 	def write(self, s):
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 		if not s: return
 		if self.pos > self.len:
 			self.buflist.append('\0'*(self.pos - self.len))
@@ -105,7 +117,8 @@
 	def writelines(self, list):
 		self.write(string.joinfields(list, ''))
 	def flush(self):
-		pass
+		if self.closed:
+			raise ValueError, "I/O operation on closed file"
 	def getvalue(self):
 		if self.buflist:
 			self.buf = self.buf + string.joinfields(self.buflist, '')
diff --git a/Lib/dos-8x3/test_lon.py b/Lib/dos-8x3/test_lon.py
new file mode 100644
index 0000000..f235d9c
--- /dev/null
+++ b/Lib/dos-8x3/test_lon.py
@@ -0,0 +1,252 @@
+from test_support import TestFailed, verbose
+from string import join
+from random import random, randint
+
+# SHIFT should match the value in longintrepr.h for best testing.
+SHIFT = 15
+BASE = 2 ** SHIFT
+MASK = BASE - 1
+
+# Max number of base BASE digits to use in test cases.  Doubling
+# this will at least quadruple the runtime.
+MAXDIGITS = 10
+
+# build some special values
+special = map(long, [0, 1, 2, BASE, BASE >> 1])
+special.append(0x5555555555555555L)
+special.append(0xaaaaaaaaaaaaaaaaL)
+#  some solid strings of one bits
+p2 = 4L  # 0 and 1 already added
+for i in range(2*SHIFT):
+    special.append(p2 - 1)
+    p2 = p2 << 1
+del p2
+# add complements & negations
+special = special + map(lambda x: ~x, special) + \
+                    map(lambda x: -x, special)
+
+# ------------------------------------------------------------ utilities
+
+# Use check instead of assert so the test still does something
+# under -O.
+
+def check(ok, *args):
+    if not ok:
+        raise TestFailed, join(map(str, args), " ")
+
+# Get quasi-random long consisting of ndigits digits (in base BASE).
+# quasi == the most-significant digit will not be 0, and the number
+# is constructed to contain long strings of 0 and 1 bits.  These are
+# more likely than random bits to provoke digit-boundary errors.
+# The sign of the number is also random.
+
+def getran(ndigits):
+    assert ndigits > 0
+    nbits_hi = ndigits * SHIFT
+    nbits_lo = nbits_hi - SHIFT + 1
+    answer = 0L
+    nbits = 0
+    r = int(random() * (SHIFT * 2)) | 1  # force 1 bits to start
+    while nbits < nbits_lo:
+        bits = (r >> 1) + 1
+        bits = min(bits, nbits_hi - nbits)
+        assert 1 <= bits <= SHIFT
+        nbits = nbits + bits
+        answer = answer << bits
+        if r & 1:
+            answer = answer | ((1 << bits) - 1)
+        r = int(random() * (SHIFT * 2))
+    assert nbits_lo <= nbits <= nbits_hi
+    if random() < 0.5:
+        answer = -answer
+    return answer
+
+# Get random long consisting of ndigits random digits (relative to base
+# BASE).  The sign bit is also random.
+
+def getran2(ndigits):
+    answer = 0L
+    for i in range(ndigits):
+        answer = (answer << SHIFT) | randint(0, MASK)
+    if random() < 0.5:
+        answer = -answer
+    return answer
+
+# --------------------------------------------------------------- divmod
+
+def test_division_2(x, y):
+    q, r = divmod(x, y)
+    q2, r2 = x/y, x%y
+    check(q == q2, "divmod returns different quotient than / for", x, y)
+    check(r == r2, "divmod returns different mod than % for", x, y)
+    check(x == q*y + r, "x != q*y + r after divmod on", x, y)
+    if y > 0:
+        check(0 <= r < y, "bad mod from divmod on", x, y)
+    else:
+        check(y < r <= 0, "bad mod from divmod on", x, y)
+
+def test_division(maxdigits=MAXDIGITS):
+    print "long / * % divmod"
+    digits = range(1, maxdigits+1)
+    for lenx in digits:
+        x = getran(lenx)
+        for leny in digits:
+            y = getran(leny) or 1L
+            test_division_2(x, y)
+
+# -------------------------------------------------------------- ~ & | ^
+
+def test_bitop_identities_1(x):
+    check(x & 0 == 0, "x & 0 != 0 for", x)
+    check(x | 0 == x, "x | 0 != x for", x)
+    check(x ^ 0 == x, "x ^ 0 != x for", x)
+    check(x & -1 == x, "x & -1 != x for", x)
+    check(x | -1 == -1, "x | -1 != -1 for", x)
+    check(x ^ -1 == ~x, "x ^ -1 != ~x for", x)
+    check(x == ~~x, "x != ~~x for", x)
+    check(x & x == x, "x & x != x for", x)
+    check(x | x == x, "x | x != x for", x)
+    check(x ^ x == 0, "x ^ x != 0 for", x)
+    check(x & ~x == 0, "x & ~x != 0 for", x)
+    check(x | ~x == -1, "x | ~x != -1 for", x)
+    check(x ^ ~x == -1, "x ^ ~x != -1 for", x)
+    check(-x == 1 + ~x == ~(x-1), "not -x == 1 + ~x == ~(x-1) for", x)
+    for n in range(2*SHIFT):
+        p2 = 2L ** n
+        check(x << n >> n == x, "x << n >> n != x for", x, n)
+        check(x / p2 == x >> n, "x / p2 != x >> n for x n p2", x, n, p2)
+        check(x * p2 == x << n, "x * p2 != x << n for x n p2", x, n, p2)
+        check(x & -p2 == x >> n << n == x & ~(p2 - 1),
+            "not x & -p2 == x >> n << n == x & ~(p2 - 1) for x n p2",
+            x, n, p2)
+
+def test_bitop_identities_2(x, y):
+    check(x & y == y & x, "x & y != y & x for", x, y)
+    check(x | y == y | x, "x | y != y | x for", x, y)
+    check(x ^ y == y ^ x, "x ^ y != y ^ x for", x, y)
+    check(x ^ y ^ x == y, "x ^ y ^ x != y for", x, y)
+    check(x & y == ~(~x | ~y), "x & y != ~(~x | ~y) for", x, y)
+    check(x | y == ~(~x & ~y), "x | y != ~(~x & ~y) for", x, y)
+    check(x ^ y == (x | y) & ~(x & y),
+         "x ^ y != (x | y) & ~(x & y) for", x, y)
+    check(x ^ y == (x & ~y) | (~x & y),
+         "x ^ y == (x & ~y) | (~x & y) for", x, y)
+    check(x ^ y == (x | y) & (~x | ~y),
+         "x ^ y == (x | y) & (~x | ~y) for", x, y)
+
+def test_bitop_identities_3(x, y, z):
+    check((x & y) & z == x & (y & z),
+         "(x & y) & z != x & (y & z) for", x, y, z)
+    check((x | y) | z == x | (y | z),
+         "(x | y) | z != x | (y | z) for", x, y, z)
+    check((x ^ y) ^ z == x ^ (y ^ z),
+         "(x ^ y) ^ z != x ^ (y ^ z) for", x, y, z)
+    check(x & (y | z) == (x & y) | (x & z),
+         "x & (y | z) != (x & y) | (x & z) for", x, y, z)
+    check(x | (y & z) == (x | y) & (x | z),
+         "x | (y & z) != (x | y) & (x | z) for", x, y, z)
+
+def test_bitop_identities(maxdigits=MAXDIGITS):
+    print "long bit-operation identities"
+    for x in special:
+        test_bitop_identities_1(x)
+    digits = range(1, maxdigits+1)
+    for lenx in digits:
+        x = getran(lenx)
+        test_bitop_identities_1(x)
+        for leny in digits:
+            y = getran(leny)
+            test_bitop_identities_2(x, y)
+            test_bitop_identities_3(x, y, getran((lenx + leny)/2))
+
+# ------------------------------------------------------ hex oct str atol
+
+def slow_format(x, base):
+    if (x, base) == (0, 8):
+        # this is an oddball!
+        return "0L"
+    digits = []
+    sign = 0
+    if x < 0:
+        sign, x = 1, -x
+    while x:
+        x, r = divmod(x, base)
+        digits.append(int(r))
+    digits.reverse()
+    digits = digits or [0]
+    return '-'[:sign] + \
+           {8: '0', 10: '', 16: '0x'}[base] + \
+           join(map(lambda i: "0123456789ABCDEF"[i], digits), '') + \
+           "L"
+
+def test_format_1(x):
+    from string import atol
+    for base, mapper in (8, oct), (10, str), (16, hex):
+        got = mapper(x)
+        expected = slow_format(x, base)
+        check(got == expected, mapper.__name__, "returned",
+              got, "but expected", expected, "for", x)
+        check(atol(got, 0) == x, 'atol("%s", 0) !=' % got, x)
+
+def test_format(maxdigits=MAXDIGITS):
+    print "long str/hex/oct/atol"
+    for x in special:
+        test_format_1(x)
+    for i in range(10):
+        for lenx in range(1, maxdigits+1):
+            x = getran(lenx)
+            test_format_1(x)
+
+# ----------------------------------------------------------------- misc
+
+def test_misc(maxdigits=MAXDIGITS):
+    print "long miscellaneous operations"
+    import sys
+
+    # check the extremes in int<->long conversion
+    hugepos = sys.maxint
+    hugeneg = -hugepos - 1
+    hugepos_aslong = long(hugepos)
+    hugeneg_aslong = long(hugeneg)
+    check(hugepos == hugepos_aslong, "long(sys.maxint) != sys.maxint")
+    check(hugeneg == hugeneg_aslong,
+        "long(-sys.maxint-1) != -sys.maxint-1")
+
+    # long -> int should not fail for hugepos_aslong or hugeneg_aslong
+    try:
+        check(int(hugepos_aslong) == hugepos,
+              "converting sys.maxint to long and back to int fails")
+    except OverflowError:
+        raise TestFailed, "int(long(sys.maxint)) overflowed!"
+    try:
+        check(int(hugeneg_aslong) == hugeneg,
+              "converting -sys.maxint-1 to long and back to int fails")
+    except OverflowError:
+        raise TestFailed, "int(long(-sys.maxint-1)) overflowed!"
+
+    # but long -> int should overflow for hugepos+1 and hugeneg-1
+    x = hugepos_aslong + 1
+    try:
+        int(x)
+        raise ValueError
+    except OverflowError:
+        pass
+    except:
+        raise TestFailed, "int(long(sys.maxint) + 1) didn't overflow"
+
+    x = hugeneg_aslong - 1
+    try:
+        int(x)
+        raise ValueError
+    except OverflowError:
+        pass
+    except:
+        raise TestFailed, "int(long(-sys.maxint-1) - 1) didn't overflow"
+
+# ---------------------------------------------------------------- do it
+
+test_division()
+test_bitop_identities()
+test_format()
+test_misc()
+
diff --git a/Lib/dos-8x3/test_mim.py b/Lib/dos-8x3/test_mim.py
new file mode 100644
index 0000000..8b74421
--- /dev/null
+++ b/Lib/dos-8x3/test_mim.py
@@ -0,0 +1,170 @@
+"""Test program for MimeWriter module.
+
+The test program was too big to comfortably fit in the MimeWriter
+class, so it's here in its own file.
+
+This should generate Barry's example, modulo some quotes and newlines.
+
+"""
+
+
+from MimeWriter import MimeWriter
+
+SELLER = '''\
+INTERFACE Seller-1;
+
+TYPE Seller = OBJECT
+    DOCUMENTATION "A simple Seller interface to test ILU"
+    METHODS
+            price():INTEGER,
+    END;
+'''
+
+BUYER = '''\
+class Buyer:
+    def __setup__(self, maxprice):
+        self._maxprice = maxprice
+
+    def __main__(self, kos):
+        """Entry point upon arrival at a new KOS."""
+        broker = kos.broker()
+        # B4 == Barry's Big Bass Business :-)
+        seller = broker.lookup('Seller_1.Seller', 'B4')
+        if seller:
+            price = seller.price()
+            print 'Seller wants $', price, '... '
+            if price > self._maxprice:
+                print 'too much!'
+            else:
+                print "I'll take it!"
+        else:
+            print 'no seller found here'
+'''                                     # Don't ask why this comment is here
+
+STATE = '''\
+# instantiate a buyer instance and put it in a magic place for the KOS
+# to find.
+__kp__ = Buyer()
+__kp__.__setup__(500)
+'''
+
+SIMPLE_METADATA = [
+        ("Interpreter", "python"),
+        ("Interpreter-Version", "1.3"),
+        ("Owner-Name", "Barry Warsaw"),
+        ("Owner-Rendezvous", "bwarsaw@cnri.reston.va.us"),
+        ("Home-KSS", "kss.cnri.reston.va.us"),
+        ("Identifier", "hdl://cnri.kss/my_first_knowbot"),
+        ("Launch-Date", "Mon Feb 12 16:39:03 EST 1996"),
+        ]
+
+COMPLEX_METADATA = [
+        ("Metadata-Type", "complex"),
+        ("Metadata-Key", "connection"),
+        ("Access", "read-only"),
+        ("Connection-Description", "Barry's Big Bass Business"),
+        ("Connection-Id", "B4"),
+        ("Connection-Direction", "client"),
+        ]
+
+EXTERNAL_METADATA = [
+        ("Metadata-Type", "complex"),
+        ("Metadata-Key", "generic-interface"),
+        ("Access", "read-only"),
+        ("Connection-Description", "Generic Interface for All Knowbots"),
+        ("Connection-Id", "generic-kp"),
+        ("Connection-Direction", "client"),
+        ]
+
+
+def main():
+    import sys
+
+    # Toplevel headers
+    
+    toplevel = MimeWriter(sys.stdout)
+    toplevel.addheader("From", "bwarsaw@cnri.reston.va.us")
+    toplevel.addheader("Date", "Mon Feb 12 17:21:48 EST 1996")
+    toplevel.addheader("To", "kss-submit@cnri.reston.va.us")
+    toplevel.addheader("MIME-Version", "1.0")
+
+    # Toplevel body parts
+    
+    f = toplevel.startmultipartbody("knowbot", "801spam999",
+                                    [("version", "0.1")], prefix=0)
+    f.write("This is a multi-part message in MIME format.\n")
+
+    # First toplevel body part: metadata
+
+    md = toplevel.nextpart()
+    md.startmultipartbody("knowbot-metadata", "802spam999")
+
+    # Metadata part 1
+    
+    md1 = md.nextpart()
+    md1.addheader("KP-Metadata-Type", "simple")
+    md1.addheader("KP-Access", "read-only")
+    m = MimeWriter(md1.startbody("message/rfc822"))
+    for key, value in SIMPLE_METADATA:
+        m.addheader("KPMD-" + key, value)
+    m.flushheaders()
+    del md1
+
+    # Metadata part 2
+
+    md2 = md.nextpart()
+    for key, value in COMPLEX_METADATA:
+        md2.addheader("KP-" + key, value)
+    f = md2.startbody("text/isl")
+    f.write(SELLER)
+    del md2
+
+    # Metadata part 3
+
+    md3 = md.nextpart()
+    f = md3.startbody("message/external-body",
+                      [("access-type", "URL"),
+                       ("URL", "hdl://cnri.kss/generic-knowbot")])
+    m = MimeWriter(f)
+    for key, value in EXTERNAL_METADATA:
+        md3.addheader("KP-" + key, value)
+    md3.startbody("text/isl")
+    # Phantom body doesn't need to be written
+
+    md.lastpart()
+
+    # Second toplevel body part: code
+
+    code = toplevel.nextpart()
+    code.startmultipartbody("knowbot-code", "803spam999")
+
+    # Code: buyer program source
+
+    buyer = code.nextpart()
+    buyer.addheader("KP-Module-Name", "BuyerKP")
+    f = buyer.startbody("text/plain")
+    f.write(BUYER)
+
+    code.lastpart()
+
+    # Third toplevel body part: state
+
+    state = toplevel.nextpart()
+    state.addheader("KP-Main-Module", "main")
+    state.startmultipartbody("knowbot-state", "804spam999")
+
+    # State: a bunch of assignments
+
+    st = state.nextpart()
+    st.addheader("KP-Module-Name", "main")
+    f = st.startbody("text/plain")
+    f.write(STATE)
+
+    state.lastpart()
+
+    # End toplevel body parts
+
+    toplevel.lastpart()
+
+
+main()
diff --git a/Lib/dos-8x3/threadin.py b/Lib/dos-8x3/threadin.py
new file mode 100644
index 0000000..767df45
--- /dev/null
+++ b/Lib/dos-8x3/threadin.py
@@ -0,0 +1,638 @@
+# threading.py:
+# Proposed new threading module, emulating a subset of Java's threading model
+
+import sys
+import time
+import thread
+import traceback
+import StringIO
+
+# Rename some stuff so "from threading import *" is safe
+
+_sys = sys
+del sys
+
+_time = time.time
+_sleep = time.sleep
+del time
+
+_start_new_thread = thread.start_new_thread
+_allocate_lock = thread.allocate_lock
+_get_ident = thread.get_ident
+del thread
+
+_print_exc = traceback.print_exc
+del traceback
+
+_StringIO = StringIO.StringIO
+del StringIO
+
+
+# Debug support (adapted from ihooks.py)
+
+_VERBOSE = 0
+
+if __debug__:
+
+    class _Verbose:
+
+        def __init__(self, verbose=None):
+            if verbose is None:
+                verbose = _VERBOSE
+            self.__verbose = verbose
+
+        def _note(self, format, *args):
+            if self.__verbose:
+                format = format % args
+                format = "%s: %s\n" % (
+                    currentThread().getName(), format)
+                _sys.stderr.write(format)
+
+else:
+    # Disable this when using "python -O"
+    class _Verbose:
+        def __init__(self, verbose=None):
+            pass
+        def _note(self, *args):
+            pass
+
+
+# Synchronization classes
+
+Lock = _allocate_lock
+
+def RLock(*args, **kwargs):
+    return apply(_RLock, args, kwargs)
+
+class _RLock(_Verbose):
+    
+    def __init__(self, verbose=None):
+        _Verbose.__init__(self, verbose)
+        self.__block = _allocate_lock()
+        self.__owner = None
+        self.__count = 0
+
+    def __repr__(self):
+        return "<%s(%s, %d)>" % (
+                self.__class__.__name__,
+                self.__owner and self.__owner.getName(),
+                self.__count)
+
+    def acquire(self, blocking=1):
+        me = currentThread()
+        if self.__owner is me:
+            self.__count = self.__count + 1
+            if __debug__:
+                self._note("%s.acquire(%s): recursive success", self, blocking)
+            return 1
+        rc = self.__block.acquire(blocking)
+        if rc:
+            self.__owner = me
+            self.__count = 1
+            if __debug__:
+                self._note("%s.acquire(%s): initial succes", self, blocking)
+        else:
+            if __debug__:
+                self._note("%s.acquire(%s): failure", self, blocking)
+        return rc
+
+    def release(self):
+        me = currentThread()
+        assert self.__owner is me, "release() of un-acquire()d lock"
+        self.__count = count = self.__count - 1
+        if not count:
+            self.__owner = None
+            self.__block.release()
+            if __debug__:
+                self._note("%s.release(): final release", self)
+        else:
+            if __debug__:
+                self._note("%s.release(): non-final release", self)
+
+    # Internal methods used by condition variables
+
+    def _acquire_restore(self, (count, owner)):
+        self.__block.acquire()
+        self.__count = count
+        self.__owner = owner
+        if __debug__:
+            self._note("%s._acquire_restore()", self)
+
+    def _release_save(self):
+        if __debug__:
+            self._note("%s._release_save()", self)
+        count = self.__count
+        self.__count = 0
+        owner = self.__owner
+        self.__owner = None
+        self.__block.release()
+        return (count, owner)
+
+    def _is_owned(self):
+        return self.__owner is currentThread()
+
+
+def Condition(*args, **kwargs):
+    return apply(_Condition, args, kwargs)
+
+class _Condition(_Verbose):
+
+    def __init__(self, lock=None, verbose=None):
+        _Verbose.__init__(self, verbose)
+        if lock is None:
+            lock = RLock()
+        self.__lock = lock
+        # Export the lock's acquire() and release() methods
+        self.acquire = lock.acquire
+        self.release = lock.release
+        # If the lock defines _release_save() and/or _acquire_restore(),
+        # these override the default implementations (which just call
+        # release() and acquire() on the lock).  Ditto for _is_owned().
+        try:
+            self._release_save = lock._release_save
+        except AttributeError:
+            pass
+        try:
+            self._acquire_restore = lock._acquire_restore
+        except AttributeError:
+            pass
+        try:
+            self._is_owned = lock._is_owned
+        except AttributeError:
+            pass
+        self.__waiters = []
+
+    def __repr__(self):
+        return "<Condition(%s, %d)>" % (self.__lock, len(self.__waiters))
+
+    def _release_save(self):
+        self.__lock.release()           # No state to save
+
+    def _acquire_restore(self, x):
+        self.__lock.acquire()           # Ignore saved state
+
+    def _is_owned(self):
+        if self.__lock.acquire(0):
+            self.__lock.release()
+            return 0
+        else:
+            return 1
+
+    def wait(self, timeout=None):
+        me = currentThread()
+        assert self._is_owned(), "wait() of un-acquire()d lock"
+        waiter = _allocate_lock()
+        waiter.acquire()
+        self.__waiters.append(waiter)
+        saved_state = self._release_save()
+        if timeout is None:
+            waiter.acquire()
+            if __debug__:
+                self._note("%s.wait(): got it", self)
+        else:
+            endtime = _time() + timeout
+            delay = 0.000001 # 1 usec
+            while 1:
+                gotit = waiter.acquire(0)
+                if gotit or _time() >= endtime:
+                    break
+                _sleep(delay)
+                if delay < 1.0:
+                    delay = delay * 2.0
+            if not gotit:
+                if __debug__:
+                    self._note("%s.wait(%s): timed out", self, timeout)
+                try:
+                    self.__waiters.remove(waiter)
+                except ValueError:
+                    pass
+            else:
+                if __debug__:
+                    self._note("%s.wait(%s): got it", self, timeout)
+        self._acquire_restore(saved_state)
+
+    def notify(self, n=1):
+        me = currentThread()
+        assert self._is_owned(), "notify() of un-acquire()d lock"
+        __waiters = self.__waiters
+        waiters = __waiters[:n]
+        if not waiters:
+            if __debug__:
+                self._note("%s.notify(): no waiters", self)
+            return
+        self._note("%s.notify(): notifying %d waiter%s", self, n,
+                   n!=1 and "s" or "")
+        for waiter in waiters:
+            waiter.release()
+            try:
+                __waiters.remove(waiter)
+            except ValueError:
+                pass
+
+    def notifyAll(self):
+        self.notify(len(self.__waiters))
+
+
+def Semaphore(*args, **kwargs):
+    return apply(_Semaphore, args, kwargs)
+
+class _Semaphore(_Verbose):
+
+    # After Tim Peters' semaphore class, but bnot quite the same (no maximum)
+
+    def __init__(self, value=1, verbose=None):
+        assert value >= 0, "Semaphore initial value must be >= 0"
+        _Verbose.__init__(self, verbose)
+        self.__cond = Condition(Lock())
+        self.__value = value
+
+    def acquire(self, blocking=1):
+        rc = 0
+        self.__cond.acquire()
+        while self.__value == 0:
+            if not blocking:
+                break
+            self.__cond.wait()
+        else:
+            self.__value = self.__value - 1
+            rc = 1
+        self.__cond.release()
+        return rc
+
+    def release(self):
+        self.__cond.acquire()
+        self.__value = self.__value + 1
+        self.__cond.notify()
+        self.__cond.release()
+
+
+def Event(*args, **kwargs):
+    return apply(_Event, args, kwargs)
+
+class _Event(_Verbose):
+
+    # After Tim Peters' event class (without is_posted())
+
+    def __init__(self, verbose=None):
+        _Verbose.__init__(self, verbose)
+        self.__cond = Condition(Lock())
+        self.__flag = 0
+
+    def isSet(self):
+        return self.__flag
+
+    def set(self):
+        self.__cond.acquire()
+        self.__flag = 1
+        self.__cond.notifyAll()
+        self.__cond.release()
+
+    def clear(self):
+        self.__cond.acquire()
+        self.__flag = 0
+        self.__cond.release()
+
+    def wait(self, timeout=None):
+        self.__cond.acquire()
+        if not self.__flag:
+            self.__cond.wait(timeout)
+        self.__cond.release()
+
+
+# Helper to generate new thread names
+_counter = 0
+def _newname(template="Thread-%d"):
+    global _counter
+    _counter = _counter + 1
+    return template % _counter
+
+# Active thread administration
+_active_limbo_lock = _allocate_lock()
+_active = {}
+_limbo = {}
+
+
+# Main class for threads
+
+class Thread(_Verbose):
+
+    __initialized = 0
+
+    def __init__(self, group=None, target=None, name=None,
+                 args=(), kwargs={}, verbose=None):
+        assert group is None, "group argument must be None for now"
+        _Verbose.__init__(self, verbose)
+        self.__target = target
+        self.__name = str(name or _newname())
+        self.__args = args
+        self.__kwargs = kwargs
+        self.__daemonic = self._set_daemon()
+        self.__started = 0
+        self.__stopped = 0
+        self.__block = Condition(Lock())
+        self.__initialized = 1
+
+    def _set_daemon(self):
+        # Overridden in _MainThread and _DummyThread
+        return currentThread().isDaemon()
+
+    def __repr__(self):
+        assert self.__initialized, "Thread.__init__() was not called"
+        status = "initial"
+        if self.__started:
+            status = "started"
+        if self.__stopped:
+            status = "stopped"
+        if self.__daemonic:
+            status = status + " daemon"
+        return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status)
+
+    def start(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        assert not self.__started, "thread already started"
+        if __debug__:
+            self._note("%s.start(): starting thread", self)
+        _active_limbo_lock.acquire()
+        _limbo[self] = self
+        _active_limbo_lock.release()
+        _start_new_thread(self.__bootstrap, ())
+        self.__started = 1
+        _sleep(0.000001)    # 1 usec, to let the thread run (Solaris hack)
+
+    def run(self):
+        if self.__target:
+            apply(self.__target, self.__args, self.__kwargs)
+
+    def __bootstrap(self):
+        try:
+            self.__started = 1
+            _active_limbo_lock.acquire()
+            _active[_get_ident()] = self
+            del _limbo[self]
+            _active_limbo_lock.release()
+            if __debug__:
+                self._note("%s.__bootstrap(): thread started", self)
+            try:
+                self.run()
+            except SystemExit:
+                if __debug__:
+                    self._note("%s.__bootstrap(): raised SystemExit", self)
+            except:
+                if __debug__:
+                    self._note("%s.__bootstrap(): unhandled exception", self)
+                s = _StringIO()
+                _print_exc(file=s)
+                _sys.stderr.write("Exception in thread %s:\n%s\n" %
+                                 (self.getName(), s.getvalue()))
+            else:
+                if __debug__:
+                    self._note("%s.__bootstrap(): normal return", self)
+        finally:
+            self.__stop()
+            self.__delete()
+
+    def __stop(self):
+        self.__block.acquire()
+        self.__stopped = 1
+        self.__block.notifyAll()
+        self.__block.release()
+
+    def __delete(self):
+        _active_limbo_lock.acquire()
+        del _active[_get_ident()]
+        _active_limbo_lock.release()
+
+    def join(self, timeout=None):
+        assert self.__initialized, "Thread.__init__() not called"
+        assert self.__started, "cannot join thread before it is started"
+        assert self is not currentThread(), "cannot join current thread"
+        if __debug__:
+            if not self.__stopped:
+                self._note("%s.join(): waiting until thread stops", self)
+        self.__block.acquire()
+        if timeout is None:
+            while not self.__stopped:
+                self.__block.wait()
+            if __debug__:
+                self._note("%s.join(): thread stopped", self)
+        else:
+            deadline = _time() + timeout
+            while not self.__stopped:
+                delay = deadline - _time()
+                if delay <= 0:
+                    if __debug__:
+                        self._note("%s.join(): timed out", self)
+                    break
+                self.__block.wait(delay)
+            else:
+                if __debug__:
+                    self._note("%s.join(): thread stopped", self)
+        self.__block.release()
+
+    def getName(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        return self.__name
+
+    def setName(self, name):
+        assert self.__initialized, "Thread.__init__() not called"
+        self.__name = str(name)
+
+    def isAlive(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        return self.__started and not self.__stopped
+    
+    def isDaemon(self):
+        assert self.__initialized, "Thread.__init__() not called"
+        return self.__daemonic
+
+    def setDaemon(self, daemonic):
+        assert self.__initialized, "Thread.__init__() not called"
+        assert not self.__started, "cannot set daemon status of active thread"
+        self.__daemonic = daemonic
+
+
+# Special thread class to represent the main thread
+# This is garbage collected through an exit handler
+
+class _MainThread(Thread):
+
+    def __init__(self):
+        Thread.__init__(self, name="MainThread")
+        self._Thread__started = 1
+        _active_limbo_lock.acquire()
+        _active[_get_ident()] = self
+        _active_limbo_lock.release()
+        try:
+            self.__oldexitfunc = _sys.exitfunc
+        except AttributeError:
+            self.__oldexitfunc = None
+        _sys.exitfunc = self.__exitfunc
+
+    def _set_daemon(self):
+        return 0
+
+    def __exitfunc(self):
+        self._Thread__stop()
+        t = _pickSomeNonDaemonThread()
+        if t:
+            if __debug__:
+                self._note("%s: waiting for other threads", self)
+        while t:
+            t.join()
+            t = _pickSomeNonDaemonThread()
+        if self.__oldexitfunc:
+            if __debug__:
+                self._note("%s: calling exit handler", self)
+            self.__oldexitfunc()
+        if __debug__:
+            self._note("%s: exiting", self)
+        self._Thread__delete()
+
+def _pickSomeNonDaemonThread():
+    for t in enumerate():
+        if not t.isDaemon() and t.isAlive():
+            return t
+    return None
+
+
+# Dummy thread class to represent threads not started here.
+# These aren't garbage collected when they die,
+# nor can they be waited for.
+# Their purpose is to return *something* from currentThread().
+# They are marked as daemon threads so we won't wait for them
+# when we exit (conform previous semantics).
+
+class _DummyThread(Thread):
+    
+    def __init__(self):
+        Thread.__init__(self, name=_newname("Dummy-%d"))
+        self.__Thread_started = 1
+        _active_limbo_lock.acquire()
+        _active[_get_ident()] = self
+        _active_limbo_lock.release()
+
+    def _set_daemon(self):
+        return 1
+
+    def join(self):
+        assert 0, "cannot join a dummy thread"
+
+
+# Global API functions
+
+def currentThread():
+    try:
+        return _active[_get_ident()]
+    except KeyError:
+        print "currentThread(): no current thread for", _get_ident()
+        return _DummyThread()
+
+def activeCount():
+    _active_limbo_lock.acquire()
+    count = len(_active) + len(_limbo)
+    _active_limbo_lock.release()
+    return count
+
+def enumerate():
+    _active_limbo_lock.acquire()
+    active = _active.values() + _limbo.values()
+    _active_limbo_lock.release()
+    return active
+
+
+# Create the main thread object
+
+_MainThread()
+
+
+# Self-test code
+
+def _test():
+
+    import random
+
+    class BoundedQueue(_Verbose):
+
+        def __init__(self, limit):
+            _Verbose.__init__(self)
+            self.mon = RLock()
+            self.rc = Condition(self.mon)
+            self.wc = Condition(self.mon)
+            self.limit = limit
+            self.queue = []
+
+        def put(self, item):
+            self.mon.acquire()
+            while len(self.queue) >= self.limit:
+                self._note("put(%s): queue full", item)
+                self.wc.wait()
+            self.queue.append(item)
+            self._note("put(%s): appended, length now %d",
+                       item, len(self.queue))
+            self.rc.notify()
+            self.mon.release()
+
+        def get(self):
+            self.mon.acquire()
+            while not self.queue:
+                self._note("get(): queue empty")
+                self.rc.wait()
+            item = self.queue[0]
+            del self.queue[0]
+            self._note("get(): got %s, %d left", item, len(self.queue))
+            self.wc.notify()
+            self.mon.release()
+            return item
+
+    class ProducerThread(Thread):
+
+        def __init__(self, queue, quota):
+            Thread.__init__(self, name="Producer")
+            self.queue = queue
+            self.quota = quota
+
+        def run(self):
+            from random import random
+            counter = 0
+            while counter < self.quota:
+                counter = counter + 1
+                self.queue.put("%s.%d" % (self.getName(), counter))
+                _sleep(random() * 0.00001)
+
+
+    class ConsumerThread(Thread):
+
+        def __init__(self, queue, count):
+            Thread.__init__(self, name="Consumer")
+            self.queue = queue
+            self.count = count
+
+        def run(self):
+            while self.count > 0:
+                item = self.queue.get()
+                print item
+                self.count = self.count - 1
+
+    import time
+
+    NP = 3
+    QL = 4
+    NI = 5
+
+    Q = BoundedQueue(QL)
+    P = []
+    for i in range(NP):
+        t = ProducerThread(Q, NI)
+        t.setName("Producer-%d" % (i+1))
+        P.append(t)
+    C = ConsumerThread(Q, NI*NP)
+    for t in P:
+        t.start()
+        _sleep(0.000001)
+    C.start()
+    for t in P:
+        t.join()
+    C.join()
+
+if __name__ == '__main__':
+    _test()