mirror of
https://github.com/AdaCore/cpython.git
synced 2026-02-12 12:57:15 -08:00
bpo-31370: Remove support for threads-less builds (#3385)
* Remove Setup.config * Always define WITH_THREAD for compatibility.
This commit is contained in:
committed by
Victor Stinner
parent
1f06a680de
commit
a6a4dc816d
@@ -344,6 +344,9 @@ Build and C API Changes
|
||||
download a copy of 32-bit Python for this purpose. (Contributed by Zachary
|
||||
Ware in :issue:`30450`.)
|
||||
|
||||
* Support for building ``--without-threads`` is removed.
|
||||
(Contributed by Antoine Pitrou in :issue:`31370`.).
|
||||
|
||||
|
||||
Deprecated
|
||||
==========
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#error "Python's source code assumes C's unsigned char is an 8-bit type."
|
||||
#endif
|
||||
|
||||
#if defined(__sgi) && defined(WITH_THREAD) && !defined(_SGI_MP_SOURCE)
|
||||
#if defined(__sgi) && !defined(_SGI_MP_SOURCE)
|
||||
#define _SGI_MP_SOURCE
|
||||
#endif
|
||||
|
||||
|
||||
@@ -183,8 +183,6 @@ PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(struct _frame *f, int exc);
|
||||
PyAPI_FUNC(PyThreadState *) PyEval_SaveThread(void);
|
||||
PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *);
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
||||
PyAPI_FUNC(int) PyEval_ThreadsInitialized(void);
|
||||
PyAPI_FUNC(void) PyEval_InitThreads(void);
|
||||
#ifndef Py_LIMITED_API
|
||||
@@ -213,15 +211,6 @@ PyAPI_FUNC(Py_ssize_t) _PyEval_RequestCodeExtraIndex(freefunc);
|
||||
#define Py_END_ALLOW_THREADS PyEval_RestoreThread(_save); \
|
||||
}
|
||||
|
||||
#else /* !WITH_THREAD */
|
||||
|
||||
#define Py_BEGIN_ALLOW_THREADS {
|
||||
#define Py_BLOCK_THREADS
|
||||
#define Py_UNBLOCK_THREADS
|
||||
#define Py_END_ALLOW_THREADS }
|
||||
|
||||
#endif /* !WITH_THREAD */
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *);
|
||||
PyAPI_FUNC(int) _PyEval_SliceIndexNotNone(PyObject *, Py_ssize_t *);
|
||||
|
||||
@@ -100,13 +100,8 @@ PyAPI_FUNC(int) PyImport_ImportFrozenModule(
|
||||
);
|
||||
|
||||
#ifndef Py_LIMITED_API
|
||||
#ifdef WITH_THREAD
|
||||
PyAPI_FUNC(void) _PyImport_AcquireLock(void);
|
||||
PyAPI_FUNC(int) _PyImport_ReleaseLock(void);
|
||||
#else
|
||||
#define _PyImport_AcquireLock()
|
||||
#define _PyImport_ReleaseLock() 1
|
||||
#endif
|
||||
|
||||
PyAPI_FUNC(void) _PyImport_ReInitLock(void);
|
||||
|
||||
|
||||
@@ -797,4 +797,12 @@ extern _invalid_parameter_handler _Py_silent_invalid_parameter_handler;
|
||||
#include <android/api-level.h>
|
||||
#endif
|
||||
|
||||
/* This macro used to tell whether Python was built with multithreading
|
||||
* enabled. Now multithreading is always enabled, but keep the macro
|
||||
* for compatibility.
|
||||
*/
|
||||
#ifndef WITH_THREAD
|
||||
#define WITH_THREAD
|
||||
#endif
|
||||
|
||||
#endif /* Py_PYPORT_H */
|
||||
|
||||
@@ -218,12 +218,10 @@ PyAPI_FUNC(void) PyThreadState_Delete(PyThreadState *);
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(void) _PyThreadState_DeleteExcept(PyThreadState *tstate);
|
||||
#endif /* !Py_LIMITED_API */
|
||||
#ifdef WITH_THREAD
|
||||
PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
|
||||
#ifndef Py_LIMITED_API
|
||||
PyAPI_FUNC(void) _PyGILState_Reinit(void);
|
||||
#endif /* !Py_LIMITED_API */
|
||||
#endif
|
||||
|
||||
/* Return the current thread state. The global interpreter lock must be held.
|
||||
* When the current thread state is NULL, this issues a fatal error (so that
|
||||
@@ -257,7 +255,6 @@ typedef
|
||||
enum {PyGILState_LOCKED, PyGILState_UNLOCKED}
|
||||
PyGILState_STATE;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
||||
/* Ensure that the current thread is ready to call the Python
|
||||
C API, regardless of the current state of Python, or of its
|
||||
@@ -319,7 +316,6 @@ PyAPI_FUNC(int) PyGILState_Check(void);
|
||||
PyAPI_FUNC(PyInterpreterState *) _PyGILState_GetInterpreterStateUnsafe(void);
|
||||
#endif
|
||||
|
||||
#endif /* #ifdef WITH_THREAD */
|
||||
|
||||
/* The implementation of sys._current_frames() Returns a dict mapping
|
||||
thread id to that thread's current frame.
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
"""Drop-in replacement for the thread module.
|
||||
|
||||
Meant to be used as a brain-dead substitute so that threaded code does
|
||||
not need to be rewritten for when the thread module is not present.
|
||||
|
||||
Suggested usage is::
|
||||
|
||||
try:
|
||||
import _thread
|
||||
except ImportError:
|
||||
import _dummy_thread as _thread
|
||||
|
||||
"""
|
||||
# Exports only things specified by thread documentation;
|
||||
# skipping obsolete synonyms allocate(), start_new(), exit_thread().
|
||||
__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
|
||||
'interrupt_main', 'LockType']
|
||||
|
||||
# A dummy value
|
||||
TIMEOUT_MAX = 2**31
|
||||
|
||||
# NOTE: this module can be imported early in the extension building process,
|
||||
# and so top level imports of other modules should be avoided. Instead, all
|
||||
# imports are done when needed on a function-by-function basis. Since threads
|
||||
# are disabled, the import lock should not be an issue anyway (??).
|
||||
|
||||
error = RuntimeError
|
||||
|
||||
def start_new_thread(function, args, kwargs={}):
|
||||
"""Dummy implementation of _thread.start_new_thread().
|
||||
|
||||
Compatibility is maintained by making sure that ``args`` is a
|
||||
tuple and ``kwargs`` is a dictionary. If an exception is raised
|
||||
and it is SystemExit (which can be done by _thread.exit()) it is
|
||||
caught and nothing is done; all other exceptions are printed out
|
||||
by using traceback.print_exc().
|
||||
|
||||
If the executed function calls interrupt_main the KeyboardInterrupt will be
|
||||
raised when the function returns.
|
||||
|
||||
"""
|
||||
if type(args) != type(tuple()):
|
||||
raise TypeError("2nd arg must be a tuple")
|
||||
if type(kwargs) != type(dict()):
|
||||
raise TypeError("3rd arg must be a dict")
|
||||
global _main
|
||||
_main = False
|
||||
try:
|
||||
function(*args, **kwargs)
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
_main = True
|
||||
global _interrupt
|
||||
if _interrupt:
|
||||
_interrupt = False
|
||||
raise KeyboardInterrupt
|
||||
|
||||
def exit():
|
||||
"""Dummy implementation of _thread.exit()."""
|
||||
raise SystemExit
|
||||
|
||||
def get_ident():
|
||||
"""Dummy implementation of _thread.get_ident().
|
||||
|
||||
Since this module should only be used when _threadmodule is not
|
||||
available, it is safe to assume that the current process is the
|
||||
only thread. Thus a constant can be safely returned.
|
||||
"""
|
||||
return 1
|
||||
|
||||
def allocate_lock():
|
||||
"""Dummy implementation of _thread.allocate_lock()."""
|
||||
return LockType()
|
||||
|
||||
def stack_size(size=None):
|
||||
"""Dummy implementation of _thread.stack_size()."""
|
||||
if size is not None:
|
||||
raise error("setting thread stack size not supported")
|
||||
return 0
|
||||
|
||||
def _set_sentinel():
|
||||
"""Dummy implementation of _thread._set_sentinel()."""
|
||||
return LockType()
|
||||
|
||||
class LockType(object):
|
||||
"""Class implementing dummy implementation of _thread.LockType.
|
||||
|
||||
Compatibility is maintained by maintaining self.locked_status
|
||||
which is a boolean that stores the state of the lock. Pickling of
|
||||
the lock, though, should not be done since if the _thread module is
|
||||
then used with an unpickled ``lock()`` from here problems could
|
||||
occur from this class not having atomic methods.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.locked_status = False
|
||||
|
||||
def acquire(self, waitflag=None, timeout=-1):
|
||||
"""Dummy implementation of acquire().
|
||||
|
||||
For blocking calls, self.locked_status is automatically set to
|
||||
True and returned appropriately based on value of
|
||||
``waitflag``. If it is non-blocking, then the value is
|
||||
actually checked and not set if it is already acquired. This
|
||||
is all done so that threading.Condition's assert statements
|
||||
aren't triggered and throw a little fit.
|
||||
|
||||
"""
|
||||
if waitflag is None or waitflag:
|
||||
self.locked_status = True
|
||||
return True
|
||||
else:
|
||||
if not self.locked_status:
|
||||
self.locked_status = True
|
||||
return True
|
||||
else:
|
||||
if timeout > 0:
|
||||
import time
|
||||
time.sleep(timeout)
|
||||
return False
|
||||
|
||||
__enter__ = acquire
|
||||
|
||||
def __exit__(self, typ, val, tb):
|
||||
self.release()
|
||||
|
||||
def release(self):
|
||||
"""Release the dummy lock."""
|
||||
# XXX Perhaps shouldn't actually bother to test? Could lead
|
||||
# to problems for complex, threaded code.
|
||||
if not self.locked_status:
|
||||
raise error
|
||||
self.locked_status = False
|
||||
return True
|
||||
|
||||
def locked(self):
|
||||
return self.locked_status
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s %s.%s object at %s>" % (
|
||||
"locked" if self.locked_status else "unlocked",
|
||||
self.__class__.__module__,
|
||||
self.__class__.__qualname__,
|
||||
hex(id(self))
|
||||
)
|
||||
|
||||
# Used to signal that interrupt_main was called in a "thread"
|
||||
_interrupt = False
|
||||
# True when not executing in a "thread"
|
||||
_main = True
|
||||
|
||||
def interrupt_main():
|
||||
"""Set _interrupt flag to True to have start_new_thread raise
|
||||
KeyboardInterrupt upon exiting."""
|
||||
if _main:
|
||||
raise KeyboardInterrupt
|
||||
else:
|
||||
global _interrupt
|
||||
_interrupt = True
|
||||
@@ -436,75 +436,34 @@ _rounding_modes = (ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_CEILING,
|
||||
# work for older Pythons. If threads are not part of the build, create a
|
||||
# mock threading object with threading.local() returning the module namespace.
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
# Python was compiled without threads; create a mock object instead
|
||||
class MockThreading(object):
|
||||
def local(self, sys=sys):
|
||||
return sys.modules[__xname__]
|
||||
threading = MockThreading()
|
||||
del MockThreading
|
||||
import threading
|
||||
|
||||
try:
|
||||
threading.local
|
||||
local = threading.local()
|
||||
if hasattr(local, '__decimal_context__'):
|
||||
del local.__decimal_context__
|
||||
|
||||
except AttributeError:
|
||||
def getcontext(_local=local):
|
||||
"""Returns this thread's context.
|
||||
|
||||
# To fix reloading, force it to create a new context
|
||||
# Old contexts have different exceptions in their dicts, making problems.
|
||||
if hasattr(threading.current_thread(), '__decimal_context__'):
|
||||
del threading.current_thread().__decimal_context__
|
||||
|
||||
def setcontext(context):
|
||||
"""Set this thread's context to context."""
|
||||
if context in (DefaultContext, BasicContext, ExtendedContext):
|
||||
context = context.copy()
|
||||
context.clear_flags()
|
||||
threading.current_thread().__decimal_context__ = context
|
||||
|
||||
def getcontext():
|
||||
"""Returns this thread's context.
|
||||
|
||||
If this thread does not yet have a context, returns
|
||||
a new context and sets this thread's context.
|
||||
New contexts are copies of DefaultContext.
|
||||
"""
|
||||
try:
|
||||
return threading.current_thread().__decimal_context__
|
||||
except AttributeError:
|
||||
context = Context()
|
||||
threading.current_thread().__decimal_context__ = context
|
||||
return context
|
||||
|
||||
else:
|
||||
|
||||
local = threading.local()
|
||||
if hasattr(local, '__decimal_context__'):
|
||||
del local.__decimal_context__
|
||||
|
||||
def getcontext(_local=local):
|
||||
"""Returns this thread's context.
|
||||
|
||||
If this thread does not yet have a context, returns
|
||||
a new context and sets this thread's context.
|
||||
New contexts are copies of DefaultContext.
|
||||
"""
|
||||
try:
|
||||
return _local.__decimal_context__
|
||||
except AttributeError:
|
||||
context = Context()
|
||||
_local.__decimal_context__ = context
|
||||
return context
|
||||
|
||||
def setcontext(context, _local=local):
|
||||
"""Set this thread's context to context."""
|
||||
if context in (DefaultContext, BasicContext, ExtendedContext):
|
||||
context = context.copy()
|
||||
context.clear_flags()
|
||||
If this thread does not yet have a context, returns
|
||||
a new context and sets this thread's context.
|
||||
New contexts are copies of DefaultContext.
|
||||
"""
|
||||
try:
|
||||
return _local.__decimal_context__
|
||||
except AttributeError:
|
||||
context = Context()
|
||||
_local.__decimal_context__ = context
|
||||
return context
|
||||
|
||||
del threading, local # Don't contaminate the namespace
|
||||
def setcontext(context, _local=local):
|
||||
"""Set this thread's context to context."""
|
||||
if context in (DefaultContext, BasicContext, ExtendedContext):
|
||||
context = context.copy()
|
||||
context.clear_flags()
|
||||
_local.__decimal_context__ = context
|
||||
|
||||
del threading, local # Don't contaminate the namespace
|
||||
|
||||
def localcontext(ctx=None):
|
||||
"""Return a context manager for a copy of the supplied context
|
||||
|
||||
@@ -9,10 +9,7 @@ import errno
|
||||
import stat
|
||||
import sys
|
||||
# Import _thread instead of threading to reduce startup cost
|
||||
try:
|
||||
from _thread import allocate_lock as Lock
|
||||
except ImportError:
|
||||
from _dummy_thread import allocate_lock as Lock
|
||||
from _thread import allocate_lock as Lock
|
||||
if sys.platform in {'win32', 'cygwin'}:
|
||||
from msvcrt import setmode as _setmode
|
||||
else:
|
||||
|
||||
@@ -19,10 +19,7 @@ from re import escape as re_escape
|
||||
from datetime import (date as datetime_date,
|
||||
timedelta as datetime_timedelta,
|
||||
timezone as datetime_timezone)
|
||||
try:
|
||||
from _thread import allocate_lock as _thread_allocate_lock
|
||||
except ImportError:
|
||||
from _dummy_thread import allocate_lock as _thread_allocate_lock
|
||||
from _thread import allocate_lock as _thread_allocate_lock
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
@@ -14,11 +14,7 @@ import io
|
||||
import os
|
||||
import warnings
|
||||
import _compression
|
||||
|
||||
try:
|
||||
from threading import RLock
|
||||
except ImportError:
|
||||
from dummy_threading import RLock
|
||||
from threading import RLock
|
||||
|
||||
from _bz2 import BZ2Compressor, BZ2Decompressor
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import unittest, os, errno
|
||||
import threading
|
||||
|
||||
from ctypes import *
|
||||
from ctypes.util import find_library
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
threading = None
|
||||
|
||||
class Test(unittest.TestCase):
|
||||
def test_open(self):
|
||||
@@ -25,26 +23,25 @@ class Test(unittest.TestCase):
|
||||
self.assertEqual(set_errno(32), errno.ENOENT)
|
||||
self.assertEqual(get_errno(), 32)
|
||||
|
||||
if threading:
|
||||
def _worker():
|
||||
set_errno(0)
|
||||
|
||||
libc = CDLL(libc_name, use_errno=False)
|
||||
if os.name == "nt":
|
||||
libc_open = libc._open
|
||||
else:
|
||||
libc_open = libc.open
|
||||
libc_open.argtypes = c_char_p, c_int
|
||||
self.assertEqual(libc_open(b"", 0), -1)
|
||||
self.assertEqual(get_errno(), 0)
|
||||
|
||||
t = threading.Thread(target=_worker)
|
||||
t.start()
|
||||
t.join()
|
||||
|
||||
self.assertEqual(get_errno(), 32)
|
||||
def _worker():
|
||||
set_errno(0)
|
||||
|
||||
libc = CDLL(libc_name, use_errno=False)
|
||||
if os.name == "nt":
|
||||
libc_open = libc._open
|
||||
else:
|
||||
libc_open = libc.open
|
||||
libc_open.argtypes = c_char_p, c_int
|
||||
self.assertEqual(libc_open(b"", 0), -1)
|
||||
self.assertEqual(get_errno(), 0)
|
||||
|
||||
t = threading.Thread(target=_worker)
|
||||
t.start()
|
||||
t.join()
|
||||
|
||||
self.assertEqual(get_errno(), 32)
|
||||
set_errno(0)
|
||||
|
||||
@unittest.skipUnless(os.name == "nt", 'Test specific to Windows')
|
||||
def test_GetLastError(self):
|
||||
dll = WinDLL("kernel32", use_last_error=True)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
"""Faux ``threading`` version using ``dummy_thread`` instead of ``thread``.
|
||||
|
||||
The module ``_dummy_threading`` is added to ``sys.modules`` in order
|
||||
to not have ``threading`` considered imported. Had ``threading`` been
|
||||
directly imported it would have made all subsequent imports succeed
|
||||
regardless of whether ``_thread`` was available which is not desired.
|
||||
|
||||
"""
|
||||
from sys import modules as sys_modules
|
||||
|
||||
import _dummy_thread
|
||||
|
||||
# Declaring now so as to not have to nest ``try``s to get proper clean-up.
|
||||
holding_thread = False
|
||||
holding_threading = False
|
||||
holding__threading_local = False
|
||||
|
||||
try:
|
||||
# Could have checked if ``_thread`` was not in sys.modules and gone
|
||||
# a different route, but decided to mirror technique used with
|
||||
# ``threading`` below.
|
||||
if '_thread' in sys_modules:
|
||||
held_thread = sys_modules['_thread']
|
||||
holding_thread = True
|
||||
# Must have some module named ``_thread`` that implements its API
|
||||
# in order to initially import ``threading``.
|
||||
sys_modules['_thread'] = sys_modules['_dummy_thread']
|
||||
|
||||
if 'threading' in sys_modules:
|
||||
# If ``threading`` is already imported, might as well prevent
|
||||
# trying to import it more than needed by saving it if it is
|
||||
# already imported before deleting it.
|
||||
held_threading = sys_modules['threading']
|
||||
holding_threading = True
|
||||
del sys_modules['threading']
|
||||
|
||||
if '_threading_local' in sys_modules:
|
||||
# If ``_threading_local`` is already imported, might as well prevent
|
||||
# trying to import it more than needed by saving it if it is
|
||||
# already imported before deleting it.
|
||||
held__threading_local = sys_modules['_threading_local']
|
||||
holding__threading_local = True
|
||||
del sys_modules['_threading_local']
|
||||
|
||||
import threading
|
||||
# Need a copy of the code kept somewhere...
|
||||
sys_modules['_dummy_threading'] = sys_modules['threading']
|
||||
del sys_modules['threading']
|
||||
sys_modules['_dummy__threading_local'] = sys_modules['_threading_local']
|
||||
del sys_modules['_threading_local']
|
||||
from _dummy_threading import *
|
||||
from _dummy_threading import __all__
|
||||
|
||||
finally:
|
||||
# Put back ``threading`` if we overwrote earlier
|
||||
|
||||
if holding_threading:
|
||||
sys_modules['threading'] = held_threading
|
||||
del held_threading
|
||||
del holding_threading
|
||||
|
||||
# Put back ``_threading_local`` if we overwrote earlier
|
||||
|
||||
if holding__threading_local:
|
||||
sys_modules['_threading_local'] = held__threading_local
|
||||
del held__threading_local
|
||||
del holding__threading_local
|
||||
|
||||
# Put back ``thread`` if we overwrote, else del the entry we made
|
||||
if holding_thread:
|
||||
sys_modules['_thread'] = held_thread
|
||||
del held_thread
|
||||
else:
|
||||
del sys_modules['_thread']
|
||||
del holding_thread
|
||||
|
||||
del _dummy_thread
|
||||
del sys_modules
|
||||
@@ -22,13 +22,7 @@ from collections import namedtuple
|
||||
from types import MappingProxyType
|
||||
from weakref import WeakKeyDictionary
|
||||
from reprlib import recursive_repr
|
||||
try:
|
||||
from _thread import RLock
|
||||
except ImportError:
|
||||
class RLock:
|
||||
'Dummy reentrant lock for builds without threads'
|
||||
def __enter__(self): pass
|
||||
def __exit__(self, exctype, excinst, exctb): pass
|
||||
from _thread import RLock
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -33,10 +33,7 @@ import datetime
|
||||
import re
|
||||
import time
|
||||
import urllib.parse, urllib.request
|
||||
try:
|
||||
import threading as _threading
|
||||
except ImportError:
|
||||
import dummy_threading as _threading
|
||||
import threading as _threading
|
||||
import http.client # only for the default HTTP port
|
||||
from calendar import timegm
|
||||
|
||||
|
||||
@@ -37,10 +37,7 @@ __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
|
||||
'warn', 'warning', 'getLogRecordFactory', 'setLogRecordFactory',
|
||||
'lastResort', 'raiseExceptions']
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError: #pragma: no cover
|
||||
threading = None
|
||||
import threading
|
||||
|
||||
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
|
||||
__status__ = "production"
|
||||
@@ -210,11 +207,7 @@ def _checkLevel(level):
|
||||
#the lock would already have been acquired - so we need an RLock.
|
||||
#The same argument applies to Loggers and Manager.loggerDict.
|
||||
#
|
||||
if threading:
|
||||
_lock = threading.RLock()
|
||||
else: #pragma: no cover
|
||||
_lock = None
|
||||
|
||||
_lock = threading.RLock()
|
||||
|
||||
def _acquireLock():
|
||||
"""
|
||||
@@ -295,7 +288,7 @@ class LogRecord(object):
|
||||
self.created = ct
|
||||
self.msecs = (ct - int(ct)) * 1000
|
||||
self.relativeCreated = (self.created - _startTime) * 1000
|
||||
if logThreads and threading:
|
||||
if logThreads:
|
||||
self.thread = threading.get_ident()
|
||||
self.threadName = threading.current_thread().name
|
||||
else: # pragma: no cover
|
||||
@@ -799,10 +792,7 @@ class Handler(Filterer):
|
||||
"""
|
||||
Acquire a thread lock for serializing access to the underlying I/O.
|
||||
"""
|
||||
if threading:
|
||||
self.lock = threading.RLock()
|
||||
else: #pragma: no cover
|
||||
self.lock = None
|
||||
self.lock = threading.RLock()
|
||||
|
||||
def acquire(self):
|
||||
"""
|
||||
|
||||
@@ -31,14 +31,9 @@ import logging.handlers
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
try:
|
||||
import _thread as thread
|
||||
import threading
|
||||
except ImportError: #pragma: no cover
|
||||
thread = None
|
||||
|
||||
from socketserver import ThreadingTCPServer, StreamRequestHandler
|
||||
|
||||
|
||||
@@ -816,8 +811,6 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
|
||||
normal. Note that you can return transformed bytes, e.g. by decrypting
|
||||
the bytes passed in.
|
||||
"""
|
||||
if not thread: #pragma: no cover
|
||||
raise NotImplementedError("listen() needs threading to work")
|
||||
|
||||
class ConfigStreamHandler(StreamRequestHandler):
|
||||
"""
|
||||
|
||||
@@ -26,10 +26,7 @@ To use, simply 'import logging.handlers' and log away!
|
||||
import logging, socket, os, pickle, struct, time, re
|
||||
from stat import ST_DEV, ST_INO, ST_MTIME
|
||||
import queue
|
||||
try:
|
||||
import threading
|
||||
except ImportError: #pragma: no cover
|
||||
threading = None
|
||||
import threading
|
||||
|
||||
#
|
||||
# Some constants...
|
||||
@@ -1395,110 +1392,110 @@ class QueueHandler(logging.Handler):
|
||||
except Exception:
|
||||
self.handleError(record)
|
||||
|
||||
if threading:
|
||||
class QueueListener(object):
|
||||
|
||||
class QueueListener(object):
|
||||
"""
|
||||
This class implements an internal threaded listener which watches for
|
||||
LogRecords being added to a queue, removes them and passes them to a
|
||||
list of handlers for processing.
|
||||
"""
|
||||
_sentinel = None
|
||||
|
||||
def __init__(self, queue, *handlers, respect_handler_level=False):
|
||||
"""
|
||||
This class implements an internal threaded listener which watches for
|
||||
LogRecords being added to a queue, removes them and passes them to a
|
||||
list of handlers for processing.
|
||||
Initialise an instance with the specified queue and
|
||||
handlers.
|
||||
"""
|
||||
_sentinel = None
|
||||
self.queue = queue
|
||||
self.handlers = handlers
|
||||
self._thread = None
|
||||
self.respect_handler_level = respect_handler_level
|
||||
|
||||
def __init__(self, queue, *handlers, respect_handler_level=False):
|
||||
"""
|
||||
Initialise an instance with the specified queue and
|
||||
handlers.
|
||||
"""
|
||||
self.queue = queue
|
||||
self.handlers = handlers
|
||||
self._thread = None
|
||||
self.respect_handler_level = respect_handler_level
|
||||
def dequeue(self, block):
|
||||
"""
|
||||
Dequeue a record and return it, optionally blocking.
|
||||
|
||||
def dequeue(self, block):
|
||||
"""
|
||||
Dequeue a record and return it, optionally blocking.
|
||||
The base implementation uses get. You may want to override this method
|
||||
if you want to use timeouts or work with custom queue implementations.
|
||||
"""
|
||||
return self.queue.get(block)
|
||||
|
||||
The base implementation uses get. You may want to override this method
|
||||
if you want to use timeouts or work with custom queue implementations.
|
||||
"""
|
||||
return self.queue.get(block)
|
||||
def start(self):
|
||||
"""
|
||||
Start the listener.
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
Start the listener.
|
||||
This starts up a background thread to monitor the queue for
|
||||
LogRecords to process.
|
||||
"""
|
||||
self._thread = t = threading.Thread(target=self._monitor)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
This starts up a background thread to monitor the queue for
|
||||
LogRecords to process.
|
||||
"""
|
||||
self._thread = t = threading.Thread(target=self._monitor)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
def prepare(self , record):
|
||||
"""
|
||||
Prepare a record for handling.
|
||||
|
||||
def prepare(self , record):
|
||||
"""
|
||||
Prepare a record for handling.
|
||||
This method just returns the passed-in record. You may want to
|
||||
override this method if you need to do any custom marshalling or
|
||||
manipulation of the record before passing it to the handlers.
|
||||
"""
|
||||
return record
|
||||
|
||||
This method just returns the passed-in record. You may want to
|
||||
override this method if you need to do any custom marshalling or
|
||||
manipulation of the record before passing it to the handlers.
|
||||
"""
|
||||
return record
|
||||
def handle(self, record):
|
||||
"""
|
||||
Handle a record.
|
||||
|
||||
def handle(self, record):
|
||||
"""
|
||||
Handle a record.
|
||||
This just loops through the handlers offering them the record
|
||||
to handle.
|
||||
"""
|
||||
record = self.prepare(record)
|
||||
for handler in self.handlers:
|
||||
if not self.respect_handler_level:
|
||||
process = True
|
||||
else:
|
||||
process = record.levelno >= handler.level
|
||||
if process:
|
||||
handler.handle(record)
|
||||
|
||||
This just loops through the handlers offering them the record
|
||||
to handle.
|
||||
"""
|
||||
record = self.prepare(record)
|
||||
for handler in self.handlers:
|
||||
if not self.respect_handler_level:
|
||||
process = True
|
||||
else:
|
||||
process = record.levelno >= handler.level
|
||||
if process:
|
||||
handler.handle(record)
|
||||
def _monitor(self):
|
||||
"""
|
||||
Monitor the queue for records, and ask the handler
|
||||
to deal with them.
|
||||
|
||||
def _monitor(self):
|
||||
"""
|
||||
Monitor the queue for records, and ask the handler
|
||||
to deal with them.
|
||||
|
||||
This method runs on a separate, internal thread.
|
||||
The thread will terminate if it sees a sentinel object in the queue.
|
||||
"""
|
||||
q = self.queue
|
||||
has_task_done = hasattr(q, 'task_done')
|
||||
while True:
|
||||
try:
|
||||
record = self.dequeue(True)
|
||||
if record is self._sentinel:
|
||||
break
|
||||
self.handle(record)
|
||||
if has_task_done:
|
||||
q.task_done()
|
||||
except queue.Empty:
|
||||
This method runs on a separate, internal thread.
|
||||
The thread will terminate if it sees a sentinel object in the queue.
|
||||
"""
|
||||
q = self.queue
|
||||
has_task_done = hasattr(q, 'task_done')
|
||||
while True:
|
||||
try:
|
||||
record = self.dequeue(True)
|
||||
if record is self._sentinel:
|
||||
break
|
||||
self.handle(record)
|
||||
if has_task_done:
|
||||
q.task_done()
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
def enqueue_sentinel(self):
|
||||
"""
|
||||
This is used to enqueue the sentinel record.
|
||||
def enqueue_sentinel(self):
|
||||
"""
|
||||
This is used to enqueue the sentinel record.
|
||||
|
||||
The base implementation uses put_nowait. You may want to override this
|
||||
method if you want to use timeouts or work with custom queue
|
||||
implementations.
|
||||
"""
|
||||
self.queue.put_nowait(self._sentinel)
|
||||
The base implementation uses put_nowait. You may want to override this
|
||||
method if you want to use timeouts or work with custom queue
|
||||
implementations.
|
||||
"""
|
||||
self.queue.put_nowait(self._sentinel)
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the listener.
|
||||
def stop(self):
|
||||
"""
|
||||
Stop the listener.
|
||||
|
||||
This asks the thread to terminate, and then waits for it to do so.
|
||||
Note that if you don't call this before your application exits, there
|
||||
may be some records still left on the queue, which won't be processed.
|
||||
"""
|
||||
self.enqueue_sentinel()
|
||||
self._thread.join()
|
||||
self._thread = None
|
||||
This asks the thread to terminate, and then waits for it to do so.
|
||||
Note that if you don't call this before your application exits, there
|
||||
may be some records still left on the queue, which won't be processed.
|
||||
"""
|
||||
self.enqueue_sentinel()
|
||||
self._thread.join()
|
||||
self._thread = None
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
'''A multi-producer, multi-consumer queue.'''
|
||||
|
||||
try:
|
||||
import threading
|
||||
except ImportError:
|
||||
import dummy_threading as threading
|
||||
import threading
|
||||
from collections import deque
|
||||
from heapq import heappush, heappop
|
||||
from time import monotonic as time
|
||||
|
||||
@@ -4,10 +4,7 @@ __all__ = ["Repr", "repr", "recursive_repr"]
|
||||
|
||||
import builtins
|
||||
from itertools import islice
|
||||
try:
|
||||
from _thread import get_ident
|
||||
except ImportError:
|
||||
from _dummy_thread import get_ident
|
||||
from _thread import get_ident
|
||||
|
||||
def recursive_repr(fillvalue='...'):
|
||||
'Decorator to make a repr function return fillvalue for a recursive call'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user