From 3dc0b82331c04199a3bad65510489e1a8278a90b Mon Sep 17 00:00:00 2001 From: mmckerns Date: Fri, 9 Feb 2024 21:41:21 -0500 Subject: [PATCH] update to python 3.11.8 and 3.12.2 --- py3.11/README_MODS | 135 ++++++++++++++++++++++ py3.11/multiprocess/managers.py | 2 +- py3.11/multiprocess/popen_spawn_win32.py | 54 +++++---- py3.11/multiprocess/resource_sharer.py | 2 +- py3.11/multiprocess/tests/__init__.py | 34 +++++- py3.11/multiprocess/util.py | 13 ++- py3.12/README_MODS | 141 +++++++++++++++++++++++ py3.12/multiprocess/managers.py | 2 +- py3.12/multiprocess/popen_spawn_win32.py | 54 +++++---- py3.12/multiprocess/queues.py | 5 + py3.12/multiprocess/resource_sharer.py | 2 +- py3.12/multiprocess/tests/__init__.py | 34 +++++- py3.12/multiprocess/util.py | 13 ++- 13 files changed, 427 insertions(+), 64 deletions(-) diff --git a/py3.11/README_MODS b/py3.11/README_MODS index 28eeb91..d100ce0 100644 --- a/py3.11/README_MODS +++ b/py3.11/README_MODS @@ -1158,3 +1158,138 @@ diff Python-3.11.6/Lib/test/_test_multiprocessing.py Python-3.11.7/Lib/test/_tes > support.print_warning(f"multiprocessing.Manager still has " > f"{multiprocessing.active_children()} " > f"active children after {dt:.1f} seconds") +# ---------------------------------------------------------------------- +diff Python-3.11.7/Lib/multiprocessing/managers.py Python-3.11.8/Lib/multiprocessing/managers.py +156c156 +< self.listener = Listener(address=address, backlog=16) +--- +> self.listener = Listener(address=address, backlog=128) +diff Python-3.11.7/Lib/multiprocessing/popen_spawn_win32.py Python-3.11.8/Lib/multiprocessing/popen_spawn_win32.py +104,115c104,117 +< if self.returncode is None: +< if timeout is None: +< msecs = _winapi.INFINITE +< else: +< msecs = max(0, int(timeout * 1000 + 0.5)) +< +< res = _winapi.WaitForSingleObject(int(self._handle), msecs) +< if res == _winapi.WAIT_OBJECT_0: +< code = _winapi.GetExitCodeProcess(self._handle) +< if code == TERMINATE: +< code = -signal.SIGTERM +< self.returncode = code +--- +> if self.returncode is not None: +> return self.returncode +> +> if timeout is None: +> msecs = _winapi.INFINITE +> else: +> msecs = max(0, int(timeout * 1000 + 0.5)) +> +> res = _winapi.WaitForSingleObject(int(self._handle), msecs) +> if res == _winapi.WAIT_OBJECT_0: +> code = _winapi.GetExitCodeProcess(self._handle) +> if code == TERMINATE: +> code = -signal.SIGTERM +> self.returncode = code +123,134c125,140 +< if self.returncode is None: +< try: +< _winapi.TerminateProcess(int(self._handle), TERMINATE) +< except PermissionError: +< # ERROR_ACCESS_DENIED (winerror 5) is received when the +< # process already died. +< code = _winapi.GetExitCodeProcess(int(self._handle)) +< if code == _winapi.STILL_ACTIVE: +< raise +< self.returncode = code +< else: +< self.returncode = -signal.SIGTERM +--- +> if self.returncode is not None: +> return +> +> try: +> _winapi.TerminateProcess(int(self._handle), TERMINATE) +> except PermissionError: +> # ERROR_ACCESS_DENIED (winerror 5) is received when the +> # process already died. +> code = _winapi.GetExitCodeProcess(int(self._handle)) +> if code == _winapi.STILL_ACTIVE: +> raise +> +> # gh-113009: Don't set self.returncode. Even if GetExitCodeProcess() +> # returns an exit code different than STILL_ACTIVE, the process can +> # still be running. Only set self.returncode once WaitForSingleObject() +> # returns WAIT_OBJECT_0 in wait(). +diff Python-3.11.7/Lib/multiprocessing/resource_sharer.py Python-3.11.8/Lib/multiprocessing/resource_sharer.py +126c126 +< self._listener = Listener(authkey=process.current_process().authkey) +--- +> self._listener = Listener(authkey=process.current_process().authkey, backlog=128) +diff Python-3.11.7/Lib/multiprocessing/util.py Python-3.11.8/Lib/multiprocessing/util.py +46c46 +< _logger.log(SUBDEBUG, msg, *args) +--- +> _logger.log(SUBDEBUG, msg, *args, stacklevel=2) +50c50 +< _logger.log(DEBUG, msg, *args) +--- +> _logger.log(DEBUG, msg, *args, stacklevel=2) +54c54 +< _logger.log(INFO, msg, *args) +--- +> _logger.log(INFO, msg, *args, stacklevel=2) +58c58 +< _logger.log(SUBWARNING, msg, *args) +--- +> _logger.log(SUBWARNING, msg, *args, stacklevel=2) +133c133,136 +< rmtree(tempdir) +--- +> def onerror(func, path, err_info): +> if not issubclass(err_info[0], FileNotFoundError): +> raise +> rmtree(tempdir, onerror=onerror) +diff Python-3.11.7/Lib/test/_test_multiprocessing.py Python-3.11.8/Lib/test/_test_multiprocessing.py +2695a2696,2703 +> sleep_time = support.LONG_TIMEOUT +> +> if self.TYPE == 'threads': +> # Thread pool workers can't be forced to quit, so if the first +> # task starts early enough, we will end up waiting for it. +> # Sleep for a shorter time, so the test doesn't block. +> sleep_time = 1 +> +2697c2705 +< args = [support.LONG_TIMEOUT for i in range(10_000)] +--- +> args = [sleep_time for i in range(10_000)] +2698a2707 +> time.sleep(0.2) # give some tasks a chance to start +4631a4641,4663 +> def test_filename(self): +> logger = multiprocessing.get_logger() +> original_level = logger.level +> try: +> logger.setLevel(util.DEBUG) +> stream = io.StringIO() +> handler = logging.StreamHandler(stream) +> logging_format = '[%(levelname)s] [%(filename)s] %(message)s' +> handler.setFormatter(logging.Formatter(logging_format)) +> logger.addHandler(handler) +> logger.info('1') +> util.info('2') +> logger.debug('3') +> filename = os.path.basename(__file__) +> log_record = stream.getvalue() +> self.assertIn(f'[INFO] [{filename}] 1', log_record) +> self.assertIn(f'[INFO] [{filename}] 2', log_record) +> self.assertIn(f'[DEBUG] [{filename}] 3', log_record) +> finally: +> logger.setLevel(original_level) +> logger.removeHandler(handler) +> handler.close() +> + diff --git a/py3.11/multiprocess/managers.py b/py3.11/multiprocess/managers.py index c4dc7c8..23c2813 100644 --- a/py3.11/multiprocess/managers.py +++ b/py3.11/multiprocess/managers.py @@ -153,7 +153,7 @@ def __init__(self, registry, address, authkey, serializer): Listener, Client = listener_client[serializer] # do authentication later - self.listener = Listener(address=address, backlog=16) + self.listener = Listener(address=address, backlog=128) self.address = self.listener.address self.id_to_obj = {'0': (None, ())} diff --git a/py3.11/multiprocess/popen_spawn_win32.py b/py3.11/multiprocess/popen_spawn_win32.py index af04430..49d4c7e 100644 --- a/py3.11/multiprocess/popen_spawn_win32.py +++ b/py3.11/multiprocess/popen_spawn_win32.py @@ -101,18 +101,20 @@ def duplicate_for_child(self, handle): return reduction.duplicate(handle, self.sentinel) def wait(self, timeout=None): - if self.returncode is None: - if timeout is None: - msecs = _winapi.INFINITE - else: - msecs = max(0, int(timeout * 1000 + 0.5)) - - res = _winapi.WaitForSingleObject(int(self._handle), msecs) - if res == _winapi.WAIT_OBJECT_0: - code = _winapi.GetExitCodeProcess(self._handle) - if code == TERMINATE: - code = -signal.SIGTERM - self.returncode = code + if self.returncode is not None: + return self.returncode + + if timeout is None: + msecs = _winapi.INFINITE + else: + msecs = max(0, int(timeout * 1000 + 0.5)) + + res = _winapi.WaitForSingleObject(int(self._handle), msecs) + if res == _winapi.WAIT_OBJECT_0: + code = _winapi.GetExitCodeProcess(self._handle) + if code == TERMINATE: + code = -signal.SIGTERM + self.returncode = code return self.returncode @@ -120,18 +122,22 @@ def poll(self): return self.wait(timeout=0) def terminate(self): - if self.returncode is None: - try: - _winapi.TerminateProcess(int(self._handle), TERMINATE) - except PermissionError: - # ERROR_ACCESS_DENIED (winerror 5) is received when the - # process already died. - code = _winapi.GetExitCodeProcess(int(self._handle)) - if code == _winapi.STILL_ACTIVE: - raise - self.returncode = code - else: - self.returncode = -signal.SIGTERM + if self.returncode is not None: + return + + try: + _winapi.TerminateProcess(int(self._handle), TERMINATE) + except PermissionError: + # ERROR_ACCESS_DENIED (winerror 5) is received when the + # process already died. + code = _winapi.GetExitCodeProcess(int(self._handle)) + if code == _winapi.STILL_ACTIVE: + raise + + # gh-113009: Don't set self.returncode. Even if GetExitCodeProcess() + # returns an exit code different than STILL_ACTIVE, the process can + # still be running. Only set self.returncode once WaitForSingleObject() + # returns WAIT_OBJECT_0 in wait(). kill = terminate diff --git a/py3.11/multiprocess/resource_sharer.py b/py3.11/multiprocess/resource_sharer.py index 6607650..b8afb0f 100644 --- a/py3.11/multiprocess/resource_sharer.py +++ b/py3.11/multiprocess/resource_sharer.py @@ -123,7 +123,7 @@ def _start(self): from .connection import Listener assert self._listener is None, "Already have Listener" util.debug('starting listener and thread for sending handles') - self._listener = Listener(authkey=process.current_process().authkey) + self._listener = Listener(authkey=process.current_process().authkey, backlog=128) self._address = self._listener.address t = threading.Thread(target=self._serve) t.daemon = True diff --git a/py3.11/multiprocess/tests/__init__.py b/py3.11/multiprocess/tests/__init__.py index fd6142d..464da79 100644 --- a/py3.11/multiprocess/tests/__init__.py +++ b/py3.11/multiprocess/tests/__init__.py @@ -2702,9 +2702,18 @@ def test_make_pool(self): def test_terminate(self): # Simulate slow tasks which take "forever" to complete + sleep_time = support.LONG_TIMEOUT + + if self.TYPE == 'threads': + # Thread pool workers can't be forced to quit, so if the first + # task starts early enough, we will end up waiting for it. + # Sleep for a shorter time, so the test doesn't block. + sleep_time = 1 + p = self.Pool(3) - args = [support.LONG_TIMEOUT for i in range(10_000)] + args = [sleep_time for i in range(10_000)] result = p.map_async(time.sleep, args, chunksize=1) + time.sleep(0.2) # give some tasks a chance to start p.terminate() p.join() @@ -4654,6 +4663,29 @@ def test_level(self): root_logger.setLevel(root_level) logger.setLevel(level=LOG_LEVEL) + def test_filename(self): + logger = multiprocessing.get_logger() + original_level = logger.level + try: + logger.setLevel(util.DEBUG) + stream = io.StringIO() + handler = logging.StreamHandler(stream) + logging_format = '[%(levelname)s] [%(filename)s] %(message)s' + handler.setFormatter(logging.Formatter(logging_format)) + logger.addHandler(handler) + logger.info('1') + util.info('2') + logger.debug('3') + filename = os.path.basename(__file__) + log_record = stream.getvalue() + self.assertIn(f'[INFO] [{filename}] 1', log_record) + self.assertIn(f'[INFO] [{filename}] 2', log_record) + self.assertIn(f'[DEBUG] [{filename}] 3', log_record) + finally: + logger.setLevel(original_level) + logger.removeHandler(handler) + handler.close() + # class _TestLoggingProcessName(BaseTestCase): # diff --git a/py3.11/multiprocess/util.py b/py3.11/multiprocess/util.py index d24182b..c46aef9 100644 --- a/py3.11/multiprocess/util.py +++ b/py3.11/multiprocess/util.py @@ -43,19 +43,19 @@ def sub_debug(msg, *args): if _logger: - _logger.log(SUBDEBUG, msg, *args) + _logger.log(SUBDEBUG, msg, *args, stacklevel=2) def debug(msg, *args): if _logger: - _logger.log(DEBUG, msg, *args) + _logger.log(DEBUG, msg, *args, stacklevel=2) def info(msg, *args): if _logger: - _logger.log(INFO, msg, *args) + _logger.log(INFO, msg, *args, stacklevel=2) def sub_warning(msg, *args): if _logger: - _logger.log(SUBWARNING, msg, *args) + _logger.log(SUBWARNING, msg, *args, stacklevel=2) def get_logger(): ''' @@ -130,7 +130,10 @@ def is_abstract_socket_namespace(address): # def _remove_temp_dir(rmtree, tempdir): - rmtree(tempdir) + def onerror(func, path, err_info): + if not issubclass(err_info[0], FileNotFoundError): + raise + rmtree(tempdir, onerror=onerror) current_process = process.current_process() # current_process() can be None if the finalizer is called diff --git a/py3.12/README_MODS b/py3.12/README_MODS index 3faf396..46c371b 100644 --- a/py3.12/README_MODS +++ b/py3.12/README_MODS @@ -1359,3 +1359,144 @@ diff Python-3.12.0rc3/Lib/test/_test_multiprocessing.py Python-3.12.1/Lib/test/_ > # gh-109706: queue.put(1) can write into the queue before queue.put(2), > # there is no synchronization in the test. > self.assertSetEqual(set(results), set([2, 1])) +# ---------------------------------------------------------------------- +diff Python-3.12.1/Lib/multiprocessing/managers.py Python-3.12.2/Lib/multiprocessing/managers.py +156c156 +< self.listener = Listener(address=address, backlog=16) +--- +> self.listener = Listener(address=address, backlog=128) +diff Python-3.12.1/Lib/multiprocessing/popen_spawn_win32.py Python-3.12.2/Lib/multiprocessing/popen_spawn_win32.py +104,115c104,117 +< if self.returncode is None: +< if timeout is None: +< msecs = _winapi.INFINITE +< else: +< msecs = max(0, int(timeout * 1000 + 0.5)) +< +< res = _winapi.WaitForSingleObject(int(self._handle), msecs) +< if res == _winapi.WAIT_OBJECT_0: +< code = _winapi.GetExitCodeProcess(self._handle) +< if code == TERMINATE: +< code = -signal.SIGTERM +< self.returncode = code +--- +> if self.returncode is not None: +> return self.returncode +> +> if timeout is None: +> msecs = _winapi.INFINITE +> else: +> msecs = max(0, int(timeout * 1000 + 0.5)) +> +> res = _winapi.WaitForSingleObject(int(self._handle), msecs) +> if res == _winapi.WAIT_OBJECT_0: +> code = _winapi.GetExitCodeProcess(self._handle) +> if code == TERMINATE: +> code = -signal.SIGTERM +> self.returncode = code +123,134c125,140 +< if self.returncode is None: +< try: +< _winapi.TerminateProcess(int(self._handle), TERMINATE) +< except PermissionError: +< # ERROR_ACCESS_DENIED (winerror 5) is received when the +< # process already died. +< code = _winapi.GetExitCodeProcess(int(self._handle)) +< if code == _winapi.STILL_ACTIVE: +< raise +< self.returncode = code +< else: +< self.returncode = -signal.SIGTERM +--- +> if self.returncode is not None: +> return +> +> try: +> _winapi.TerminateProcess(int(self._handle), TERMINATE) +> except PermissionError: +> # ERROR_ACCESS_DENIED (winerror 5) is received when the +> # process already died. +> code = _winapi.GetExitCodeProcess(int(self._handle)) +> if code == _winapi.STILL_ACTIVE: +> raise +> +> # gh-113009: Don't set self.returncode. Even if GetExitCodeProcess() +> # returns an exit code different than STILL_ACTIVE, the process can +> # still be running. Only set self.returncode once WaitForSingleObject() +> # returns WAIT_OBJECT_0 in wait(). +diff Python-3.12.1/Lib/multiprocessing/queues.py Python-3.12.2/Lib/multiprocessing/queues.py +166a167,171 +> # gh-107219: Close the connection writer which can unblock +> # Queue._feed() if it was stuck in send_bytes(). +> if sys.platform == 'win32': +> self._writer.close() +> +diff Python-3.12.1/Lib/multiprocessing/resource_sharer.py Python-3.12.2/Lib/multiprocessing/resource_sharer.py +126c126 +< self._listener = Listener(authkey=process.current_process().authkey) +--- +> self._listener = Listener(authkey=process.current_process().authkey, backlog=128) +diff Python-3.12.1/Lib/multiprocessing/util.py Python-3.12.2/Lib/multiprocessing/util.py +46c46 +< _logger.log(SUBDEBUG, msg, *args) +--- +> _logger.log(SUBDEBUG, msg, *args, stacklevel=2) +50c50 +< _logger.log(DEBUG, msg, *args) +--- +> _logger.log(DEBUG, msg, *args, stacklevel=2) +54c54 +< _logger.log(INFO, msg, *args) +--- +> _logger.log(INFO, msg, *args, stacklevel=2) +58c58 +< _logger.log(SUBWARNING, msg, *args) +--- +> _logger.log(SUBWARNING, msg, *args, stacklevel=2) +133c133,136 +< rmtree(tempdir) +--- +> def onerror(func, path, err_info): +> if not issubclass(err_info[0], FileNotFoundError): +> raise +> rmtree(tempdir, onerror=onerror) +diff Python-3.12.1/Lib/test/_test_multiprocessing.py Python-3.12.2/Lib/test/_test_multiprocessing.py +2696a2697,2704 +> sleep_time = support.LONG_TIMEOUT +> +> if self.TYPE == 'threads': +> # Thread pool workers can't be forced to quit, so if the first +> # task starts early enough, we will end up waiting for it. +> # Sleep for a shorter time, so the test doesn't block. +> sleep_time = 1 +> +2698c2706 +< args = [support.LONG_TIMEOUT for i in range(10_000)] +--- +> args = [sleep_time for i in range(10_000)] +2699a2708 +> time.sleep(0.2) # give some tasks a chance to start +4632a4642,4664 +> def test_filename(self): +> logger = multiprocessing.get_logger() +> original_level = logger.level +> try: +> logger.setLevel(util.DEBUG) +> stream = io.StringIO() +> handler = logging.StreamHandler(stream) +> logging_format = '[%(levelname)s] [%(filename)s] %(message)s' +> handler.setFormatter(logging.Formatter(logging_format)) +> logger.addHandler(handler) +> logger.info('1') +> util.info('2') +> logger.debug('3') +> filename = os.path.basename(__file__) +> log_record = stream.getvalue() +> self.assertIn(f'[INFO] [{filename}] 1', log_record) +> self.assertIn(f'[INFO] [{filename}] 2', log_record) +> self.assertIn(f'[DEBUG] [{filename}] 3', log_record) +> finally: +> logger.setLevel(original_level) +> logger.removeHandler(handler) +> handler.close() +> diff --git a/py3.12/multiprocess/managers.py b/py3.12/multiprocess/managers.py index cccf118..faac40e 100644 --- a/py3.12/multiprocess/managers.py +++ b/py3.12/multiprocess/managers.py @@ -153,7 +153,7 @@ def __init__(self, registry, address, authkey, serializer): Listener, Client = listener_client[serializer] # do authentication later - self.listener = Listener(address=address, backlog=16) + self.listener = Listener(address=address, backlog=128) self.address = self.listener.address self.id_to_obj = {'0': (None, ())} diff --git a/py3.12/multiprocess/popen_spawn_win32.py b/py3.12/multiprocess/popen_spawn_win32.py index af04430..49d4c7e 100644 --- a/py3.12/multiprocess/popen_spawn_win32.py +++ b/py3.12/multiprocess/popen_spawn_win32.py @@ -101,18 +101,20 @@ def duplicate_for_child(self, handle): return reduction.duplicate(handle, self.sentinel) def wait(self, timeout=None): - if self.returncode is None: - if timeout is None: - msecs = _winapi.INFINITE - else: - msecs = max(0, int(timeout * 1000 + 0.5)) - - res = _winapi.WaitForSingleObject(int(self._handle), msecs) - if res == _winapi.WAIT_OBJECT_0: - code = _winapi.GetExitCodeProcess(self._handle) - if code == TERMINATE: - code = -signal.SIGTERM - self.returncode = code + if self.returncode is not None: + return self.returncode + + if timeout is None: + msecs = _winapi.INFINITE + else: + msecs = max(0, int(timeout * 1000 + 0.5)) + + res = _winapi.WaitForSingleObject(int(self._handle), msecs) + if res == _winapi.WAIT_OBJECT_0: + code = _winapi.GetExitCodeProcess(self._handle) + if code == TERMINATE: + code = -signal.SIGTERM + self.returncode = code return self.returncode @@ -120,18 +122,22 @@ def poll(self): return self.wait(timeout=0) def terminate(self): - if self.returncode is None: - try: - _winapi.TerminateProcess(int(self._handle), TERMINATE) - except PermissionError: - # ERROR_ACCESS_DENIED (winerror 5) is received when the - # process already died. - code = _winapi.GetExitCodeProcess(int(self._handle)) - if code == _winapi.STILL_ACTIVE: - raise - self.returncode = code - else: - self.returncode = -signal.SIGTERM + if self.returncode is not None: + return + + try: + _winapi.TerminateProcess(int(self._handle), TERMINATE) + except PermissionError: + # ERROR_ACCESS_DENIED (winerror 5) is received when the + # process already died. + code = _winapi.GetExitCodeProcess(int(self._handle)) + if code == _winapi.STILL_ACTIVE: + raise + + # gh-113009: Don't set self.returncode. Even if GetExitCodeProcess() + # returns an exit code different than STILL_ACTIVE, the process can + # still be running. Only set self.returncode once WaitForSingleObject() + # returns WAIT_OBJECT_0 in wait(). kill = terminate diff --git a/py3.12/multiprocess/queues.py b/py3.12/multiprocess/queues.py index ae51ba9..12ed1c5 100644 --- a/py3.12/multiprocess/queues.py +++ b/py3.12/multiprocess/queues.py @@ -167,6 +167,11 @@ def _terminate_broken(self): # gh-94777: Prevent queue writing to a pipe which is no longer read. self._reader.close() + # gh-107219: Close the connection writer which can unblock + # Queue._feed() if it was stuck in send_bytes(). + if sys.platform == 'win32': + self._writer.close() + self.close() self.join_thread() diff --git a/py3.12/multiprocess/resource_sharer.py b/py3.12/multiprocess/resource_sharer.py index 6607650..b8afb0f 100644 --- a/py3.12/multiprocess/resource_sharer.py +++ b/py3.12/multiprocess/resource_sharer.py @@ -123,7 +123,7 @@ def _start(self): from .connection import Listener assert self._listener is None, "Already have Listener" util.debug('starting listener and thread for sending handles') - self._listener = Listener(authkey=process.current_process().authkey) + self._listener = Listener(authkey=process.current_process().authkey, backlog=128) self._address = self._listener.address t = threading.Thread(target=self._serve) t.daemon = True diff --git a/py3.12/multiprocess/tests/__init__.py b/py3.12/multiprocess/tests/__init__.py index 166eab0..0caae93 100644 --- a/py3.12/multiprocess/tests/__init__.py +++ b/py3.12/multiprocess/tests/__init__.py @@ -2702,9 +2702,18 @@ def test_make_pool(self): def test_terminate(self): # Simulate slow tasks which take "forever" to complete + sleep_time = support.LONG_TIMEOUT + + if self.TYPE == 'threads': + # Thread pool workers can't be forced to quit, so if the first + # task starts early enough, we will end up waiting for it. + # Sleep for a shorter time, so the test doesn't block. + sleep_time = 1 + p = self.Pool(3) - args = [support.LONG_TIMEOUT for i in range(10_000)] + args = [sleep_time for i in range(10_000)] result = p.map_async(time.sleep, args, chunksize=1) + time.sleep(0.2) # give some tasks a chance to start p.terminate() p.join() @@ -4641,6 +4650,29 @@ def test_level(self): root_logger.setLevel(root_level) logger.setLevel(level=LOG_LEVEL) + def test_filename(self): + logger = multiprocessing.get_logger() + original_level = logger.level + try: + logger.setLevel(util.DEBUG) + stream = io.StringIO() + handler = logging.StreamHandler(stream) + logging_format = '[%(levelname)s] [%(filename)s] %(message)s' + handler.setFormatter(logging.Formatter(logging_format)) + logger.addHandler(handler) + logger.info('1') + util.info('2') + logger.debug('3') + filename = os.path.basename(__file__) + log_record = stream.getvalue() + self.assertIn(f'[INFO] [{filename}] 1', log_record) + self.assertIn(f'[INFO] [{filename}] 2', log_record) + self.assertIn(f'[DEBUG] [{filename}] 3', log_record) + finally: + logger.setLevel(original_level) + logger.removeHandler(handler) + handler.close() + # class _TestLoggingProcessName(BaseTestCase): # diff --git a/py3.12/multiprocess/util.py b/py3.12/multiprocess/util.py index d24182b..c46aef9 100644 --- a/py3.12/multiprocess/util.py +++ b/py3.12/multiprocess/util.py @@ -43,19 +43,19 @@ def sub_debug(msg, *args): if _logger: - _logger.log(SUBDEBUG, msg, *args) + _logger.log(SUBDEBUG, msg, *args, stacklevel=2) def debug(msg, *args): if _logger: - _logger.log(DEBUG, msg, *args) + _logger.log(DEBUG, msg, *args, stacklevel=2) def info(msg, *args): if _logger: - _logger.log(INFO, msg, *args) + _logger.log(INFO, msg, *args, stacklevel=2) def sub_warning(msg, *args): if _logger: - _logger.log(SUBWARNING, msg, *args) + _logger.log(SUBWARNING, msg, *args, stacklevel=2) def get_logger(): ''' @@ -130,7 +130,10 @@ def is_abstract_socket_namespace(address): # def _remove_temp_dir(rmtree, tempdir): - rmtree(tempdir) + def onerror(func, path, err_info): + if not issubclass(err_info[0], FileNotFoundError): + raise + rmtree(tempdir, onerror=onerror) current_process = process.current_process() # current_process() can be None if the finalizer is called