Patch #1731049: make threading.py use a proper "raise" when checking internal state, rather than assert statements (which get stripped out by -O).
diff --git a/Lib/threading.py b/Lib/threading.py
index fecd3cc..a46090d 100644
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -111,8 +111,8 @@
     __enter__ = acquire
 
     def release(self):
-        me = currentThread()
-        assert self.__owner is me, "release() of un-acquire()d lock"
+        if self.__owner is not currentThread():
+            raise RuntimeError("cannot release un-aquired lock")
         self.__count = count = self.__count - 1
         if not count:
             self.__owner = None
@@ -204,7 +204,8 @@
             return True
 
     def wait(self, timeout=None):
-        assert self._is_owned(), "wait() of un-acquire()d lock"
+        if not self._is_owned():
+            raise RuntimeError("cannot wait on un-aquired lock")
         waiter = _allocate_lock()
         waiter.acquire()
         self.__waiters.append(waiter)
@@ -245,7 +246,8 @@
             self._acquire_restore(saved_state)
 
     def notify(self, n=1):
-        assert self._is_owned(), "notify() of un-acquire()d lock"
+        if not self._is_owned():
+            raise RuntimeError("cannot notify on un-aquired lock")
         __waiters = self.__waiters
         waiters = __waiters[:n]
         if not waiters:
@@ -273,7 +275,8 @@
     # After Tim Peters' semaphore class, but not quite the same (no maximum)
 
     def __init__(self, value=1, verbose=None):
-        assert value >= 0, "Semaphore initial value must be >= 0"
+        if value < 0:
+            raise ValueError("semaphore initial value must be >= 0")
         _Verbose.__init__(self, verbose)
         self.__cond = Condition(Lock())
         self.__value = value
@@ -424,8 +427,10 @@
         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 not self.__initialized:
+            raise RuntimeError("thread.__init__() not called")
+        if self.__started:
+            raise RuntimeError("thread already started")
         if __debug__:
             self._note("%s.start(): starting thread", self)
         _active_limbo_lock.acquire()
@@ -545,9 +550,13 @@
             _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 not self.__initialized:
+            raise RuntimeError("Thread.__init__() not called")
+        if not self.__started:
+            raise RuntimeError("cannot join thread before it is started")
+        if self is currentThread():
+            raise RuntimeError("cannot join current thread")
+
         if __debug__:
             if not self.__stopped:
                 self._note("%s.join(): waiting until thread stops", self)
@@ -590,8 +599,10 @@
         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"
+        if not self.__initialized:
+            raise RuntimeError("Thread.__init__() not called")
+        if self.__started:
+            raise RuntimeError("cannot set daemon status of active thread");
         self.__daemonic = daemonic
 
 # The timer class was contributed by Itamar Shtull-Trauring