[Python-checkins] gh-118868: logging QueueHandler fix passing of kwargs (GH-118869)
https://github.com/python/cpython/commit/dce14bb2dce7887df40ae5c13b0d13e0dafceff7
commit: dce14bb2dce7887df40ae5c13b0d13e0dafceff7
branch: main
author: Kaundur
committer: vsajip
date: 2024-06-04T12:48:05+01:00
summary:
gh-118868: logging QueueHandler fix passing of kwargs (GH-118869)
Co-authored-by: Nice Zombies
Co-authored-by: Vinay Sajip
files:
A Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst
M Lib/logging/config.py
M Lib/test/test_logging.py
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 860e4751207470..ac45d6809c805c 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -725,16 +725,16 @@ def add_filters(self, filterer, filters):
def _configure_queue_handler(self, klass, **kwargs):
if 'queue' in kwargs:
-q = kwargs['queue']
+q = kwargs.pop('queue')
else:
q = queue.Queue() # unbounded
-rhl = kwargs.get('respect_handler_level', False)
-if 'listener' in kwargs:
-lklass = kwargs['listener']
-else:
-lklass = logging.handlers.QueueListener
-listener = lklass(q, *kwargs.get('handlers', []),
respect_handler_level=rhl)
-handler = klass(q)
+
+rhl = kwargs.pop('respect_handler_level', False)
+lklass = kwargs.pop('listener', logging.handlers.QueueListener)
+handlers = kwargs.pop('handlers', [])
+
+listener = lklass(q, *handlers, respect_handler_level=rhl)
+handler = klass(q, **kwargs)
handler.listener = listener
return handler
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 97d7c9fb167ec1..9ebd3457a18d68 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3976,6 +3976,35 @@ def test_111615(self):
}
logging.config.dictConfig(config)
+# gh-118868: check if kwargs are passed to logging QueueHandler
+def test_kwargs_passing(self):
+class CustomQueueHandler(logging.handlers.QueueHandler):
+def __init__(self, *args, **kwargs):
+super().__init__(queue.Queue())
+self.custom_kwargs = kwargs
+
+custom_kwargs = {'foo': 'bar'}
+
+config = {
+'version': 1,
+'handlers': {
+'custom': {
+'class': CustomQueueHandler,
+**custom_kwargs
+},
+},
+'root': {
+'level': 'DEBUG',
+'handlers': ['custom']
+}
+}
+
+logging.config.dictConfig(config)
+
+handler = logging.getHandlerByName('custom')
+self.assertEqual(handler.custom_kwargs, custom_kwargs)
+
+
class ManagerTest(BaseTest):
def test_manager_loggerclass(self):
logged = []
diff --git
a/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst
b/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst
new file mode 100644
index 00..372a809d9594b0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst
@@ -0,0 +1,2 @@
+Fixed issue where kwargs were no longer passed to the logging handler
+QueueHandler
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-118868: logging QueueHandler fix passing of kwargs (GH-118869) (GH-120031)
https://github.com/python/cpython/commit/fe68908c54103f391daa22963b017843ec63d63e commit: fe68908c54103f391daa22963b017843ec63d63e branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-04T13:17:46+01:00 summary: [3.12] gh-118868: logging QueueHandler fix passing of kwargs (GH-118869) (GH-120031) (cherry picked from commit dce14bb2dce7887df40ae5c13b0d13e0dafceff7) files: A Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 2c0d30ccf80692..3aec8361aeff6e 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -732,16 +732,16 @@ def add_filters(self, filterer, filters): def _configure_queue_handler(self, klass, **kwargs): if 'queue' in kwargs: -q = kwargs['queue'] +q = kwargs.pop('queue') else: q = queue.Queue() # unbounded -rhl = kwargs.get('respect_handler_level', False) -if 'listener' in kwargs: -lklass = kwargs['listener'] -else: -lklass = logging.handlers.QueueListener -listener = lklass(q, *kwargs.get('handlers', []), respect_handler_level=rhl) -handler = klass(q) + +rhl = kwargs.pop('respect_handler_level', False) +lklass = kwargs.pop('listener', logging.handlers.QueueListener) +handlers = kwargs.pop('handlers', []) + +listener = lklass(q, *handlers, respect_handler_level=rhl) +handler = klass(q, **kwargs) handler.listener = listener return handler diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 5a9772dffafb85..fa455035e5c41d 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3944,6 +3944,35 @@ def test_111615(self): } logging.config.dictConfig(config) +# gh-118868: check if kwargs are passed to logging QueueHandler +def test_kwargs_passing(self): +class CustomQueueHandler(logging.handlers.QueueHandler): +def __init__(self, *args, **kwargs): +super().__init__(queue.Queue()) +self.custom_kwargs = kwargs + +custom_kwargs = {'foo': 'bar'} + +config = { +'version': 1, +'handlers': { +'custom': { +'class': CustomQueueHandler, +**custom_kwargs +}, +}, +'root': { +'level': 'DEBUG', +'handlers': ['custom'] +} +} + +logging.config.dictConfig(config) + +handler = logging.getHandlerByName('custom') +self.assertEqual(handler.custom_kwargs, custom_kwargs) + + class ManagerTest(BaseTest): def test_manager_loggerclass(self): logged = [] diff --git a/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst b/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst new file mode 100644 index 00..372a809d9594b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst @@ -0,0 +1,2 @@ +Fixed issue where kwargs were no longer passed to the logging handler +QueueHandler ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-118868: logging QueueHandler fix passing of kwargs (GH-118869) (GH-120032)
https://github.com/python/cpython/commit/feaecf8c33444d44a5a554680f270c5c614185d3 commit: feaecf8c33444d44a5a554680f270c5c614185d3 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-04T13:18:11+01:00 summary: [3.13] gh-118868: logging QueueHandler fix passing of kwargs (GH-118869) (GH-120032) (cherry picked from commit dce14bb2dce7887df40ae5c13b0d13e0dafceff7) files: A Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 860e4751207470..ac45d6809c805c 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -725,16 +725,16 @@ def add_filters(self, filterer, filters): def _configure_queue_handler(self, klass, **kwargs): if 'queue' in kwargs: -q = kwargs['queue'] +q = kwargs.pop('queue') else: q = queue.Queue() # unbounded -rhl = kwargs.get('respect_handler_level', False) -if 'listener' in kwargs: -lklass = kwargs['listener'] -else: -lklass = logging.handlers.QueueListener -listener = lklass(q, *kwargs.get('handlers', []), respect_handler_level=rhl) -handler = klass(q) + +rhl = kwargs.pop('respect_handler_level', False) +lklass = kwargs.pop('listener', logging.handlers.QueueListener) +handlers = kwargs.pop('handlers', []) + +listener = lklass(q, *handlers, respect_handler_level=rhl) +handler = klass(q, **kwargs) handler.listener = listener return handler diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 97d7c9fb167ec1..9ebd3457a18d68 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3976,6 +3976,35 @@ def test_111615(self): } logging.config.dictConfig(config) +# gh-118868: check if kwargs are passed to logging QueueHandler +def test_kwargs_passing(self): +class CustomQueueHandler(logging.handlers.QueueHandler): +def __init__(self, *args, **kwargs): +super().__init__(queue.Queue()) +self.custom_kwargs = kwargs + +custom_kwargs = {'foo': 'bar'} + +config = { +'version': 1, +'handlers': { +'custom': { +'class': CustomQueueHandler, +**custom_kwargs +}, +}, +'root': { +'level': 'DEBUG', +'handlers': ['custom'] +} +} + +logging.config.dictConfig(config) + +handler = logging.getHandlerByName('custom') +self.assertEqual(handler.custom_kwargs, custom_kwargs) + + class ManagerTest(BaseTest): def test_manager_loggerclass(self): logged = [] diff --git a/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst b/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst new file mode 100644 index 00..372a809d9594b0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-09-21-36-11.gh-issue-118868.uckxxP.rst @@ -0,0 +1,2 @@ +Fixed issue where kwargs were no longer passed to the logging handler +QueueHandler ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030)
https://github.com/python/cpython/commit/99d945c0c006e3246ac00338e37c443c6e08fc5c
commit: 99d945c0c006e3246ac00338e37c443c6e08fc5c
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-06-04T13:20:50+01:00
summary:
gh-119819: Fix regression to allow logging configuration with multipr…
(GH-120030)
files:
A Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst
M Lib/logging/config.py
M Lib/test/test_logging.py
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index ac45d6809c805c..0b10bf82b60a36 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -781,8 +781,10 @@ def configure_handler(self, config):
# raise ValueError('No handlers specified for a
QueueHandler')
if 'queue' in config:
from multiprocessing.queues import Queue as MPQueue
+from multiprocessing import Manager as MM
+proxy_queue = MM().Queue()
qspec = config['queue']
-if not isinstance(qspec, (queue.Queue, MPQueue)):
+if not isinstance(qspec, (queue.Queue, MPQueue,
type(proxy_queue))):
if isinstance(qspec, str):
q = self.resolve(qspec)
if not callable(q):
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 9ebd3457a18d68..d3e5ac2be2e21e 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3926,6 +3926,32 @@ def test_config_queue_handler(self):
msg = str(ctx.exception)
self.assertEqual(msg, "Unable to configure handler 'ah'")
[email protected](support.is_wasi, "WASI does not have multiprocessing.")
+def test_multiprocessing_queues(self):
+# See gh-119819
+cd = copy.deepcopy(self.config_queue_handler)
+from multiprocessing import Queue as MQ, Manager as MM
+q1 = MQ() # this can't be pickled
+q2 = MM().Queue() # a proxy queue for use when pickling is needed
+for qspec in (q1, q2):
+fn = make_temp_file('.log', 'test_logging-cmpqh-')
+cd['handlers']['h1']['filename'] = fn
+cd['handlers']['ah']['queue'] = qspec
+qh = None
+try:
+self.apply_config(cd)
+qh = logging.getHandlerByName('ah')
+self.assertEqual(sorted(logging.getHandlerNames()), ['ah',
'h1'])
+self.assertIsNotNone(qh.listener)
+self.assertIs(qh.queue, qspec)
+self.assertIs(qh.listener.queue, qspec)
+finally:
+h = logging.getHandlerByName('h1')
+if h:
+self.addCleanup(closeFileHandler, h, fn)
+else:
+self.addCleanup(os.remove, fn)
+
def test_90195(self):
# See gh-90195
config = {
diff --git
a/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst
b/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst
new file mode 100644
index 00..f9e49c00f671f2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst
@@ -0,0 +1,2 @@
+Fix regression to allow logging configuration with multiprocessing queue
+types.
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030) (GH-120034)
https://github.com/python/cpython/commit/0e150c36dee1734a6a60f1ebd924e90fb3fe1458 commit: 0e150c36dee1734a6a60f1ebd924e90fb3fe1458 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-04T13:41:02+01:00 summary: [3.12] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030) (GH-120034) (cherry picked from commit 99d945c0c006e3246ac00338e37c443c6e08fc5c) files: A Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 3aec8361aeff6e..c98eb6c8215d1a 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -788,8 +788,10 @@ def configure_handler(self, config): # raise ValueError('No handlers specified for a QueueHandler') if 'queue' in config: from multiprocessing.queues import Queue as MPQueue +from multiprocessing import Manager as MM +proxy_queue = MM().Queue() qspec = config['queue'] -if not isinstance(qspec, (queue.Queue, MPQueue)): +if not isinstance(qspec, (queue.Queue, MPQueue, type(proxy_queue))): if isinstance(qspec, str): q = self.resolve(qspec) if not callable(q): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index fa455035e5c41d..d2a392bed1c8a7 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3894,6 +3894,32 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") [email protected](support.is_wasi, "WASI does not have multiprocessing.") +def test_multiprocessing_queues(self): +# See gh-119819 +cd = copy.deepcopy(self.config_queue_handler) +from multiprocessing import Queue as MQ, Manager as MM +q1 = MQ() # this can't be pickled +q2 = MM().Queue() # a proxy queue for use when pickling is needed +for qspec in (q1, q2): +fn = make_temp_file('.log', 'test_logging-cmpqh-') +cd['handlers']['h1']['filename'] = fn +cd['handlers']['ah']['queue'] = qspec +qh = None +try: +self.apply_config(cd) +qh = logging.getHandlerByName('ah') +self.assertEqual(sorted(logging.getHandlerNames()), ['ah', 'h1']) +self.assertIsNotNone(qh.listener) +self.assertIs(qh.queue, qspec) +self.assertIs(qh.listener.queue, qspec) +finally: +h = logging.getHandlerByName('h1') +if h: +self.addCleanup(closeFileHandler, h, fn) +else: +self.addCleanup(os.remove, fn) + def test_90195(self): # See gh-90195 config = { diff --git a/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst b/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst new file mode 100644 index 00..f9e49c00f671f2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst @@ -0,0 +1,2 @@ +Fix regression to allow logging configuration with multiprocessing queue +types. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030) (GH-120035)
https://github.com/python/cpython/commit/720a44d414b68943c766716145d7c6d15ea213de commit: 720a44d414b68943c766716145d7c6d15ea213de branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-04T13:46:41+01:00 summary: [3.13] gh-119819: Fix regression to allow logging configuration with multipr… (GH-120030) (GH-120035) (cherry picked from commit 99d945c0c006e3246ac00338e37c443c6e08fc5c) files: A Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index ac45d6809c805c..0b10bf82b60a36 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -781,8 +781,10 @@ def configure_handler(self, config): # raise ValueError('No handlers specified for a QueueHandler') if 'queue' in config: from multiprocessing.queues import Queue as MPQueue +from multiprocessing import Manager as MM +proxy_queue = MM().Queue() qspec = config['queue'] -if not isinstance(qspec, (queue.Queue, MPQueue)): +if not isinstance(qspec, (queue.Queue, MPQueue, type(proxy_queue))): if isinstance(qspec, str): q = self.resolve(qspec) if not callable(q): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 9ebd3457a18d68..d3e5ac2be2e21e 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3926,6 +3926,32 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") [email protected](support.is_wasi, "WASI does not have multiprocessing.") +def test_multiprocessing_queues(self): +# See gh-119819 +cd = copy.deepcopy(self.config_queue_handler) +from multiprocessing import Queue as MQ, Manager as MM +q1 = MQ() # this can't be pickled +q2 = MM().Queue() # a proxy queue for use when pickling is needed +for qspec in (q1, q2): +fn = make_temp_file('.log', 'test_logging-cmpqh-') +cd['handlers']['h1']['filename'] = fn +cd['handlers']['ah']['queue'] = qspec +qh = None +try: +self.apply_config(cd) +qh = logging.getHandlerByName('ah') +self.assertEqual(sorted(logging.getHandlerNames()), ['ah', 'h1']) +self.assertIsNotNone(qh.listener) +self.assertIs(qh.queue, qspec) +self.assertIs(qh.listener.queue, qspec) +finally: +h = logging.getHandlerByName('h1') +if h: +self.addCleanup(closeFileHandler, h, fn) +else: +self.addCleanup(os.remove, fn) + def test_90195(self): # See gh-90195 config = { diff --git a/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst b/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst new file mode 100644 index 00..f9e49c00f671f2 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-06-04-12-23-01.gh-issue-119819.WKKrYh.rst @@ -0,0 +1,2 @@ +Fix regression to allow logging configuration with multiprocessing queue +types. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067)
https://github.com/python/cpython/commit/109e1082ea92f89d42cd70f2cc7ca6fba6be9bab commit: 109e1082ea92f89d42cd70f2cc7ca6fba6be9bab branch: main author: Vinay Sajip committer: vsajip date: 2024-06-04T20:16:43+01:00 summary: gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) files: M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index d3e5ac2be2e21e..0c9a24e58dfd8c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3926,9 +3926,9 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") [email protected](support.is_wasi, "WASI does not have multiprocessing.") def test_multiprocessing_queues(self): # See gh-119819 +import_helper.import_module('_multiprocessing') # will skip test if it's not available cd = copy.deepcopy(self.config_queue_handler) from multiprocessing import Queue as MQ, Manager as MM q1 = MQ() # this can't be pickled ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) (GH-120071)
https://github.com/python/cpython/commit/008f9dd02711a2129b3b6272eccbce21b1a9a38b commit: 008f9dd02711a2129b3b6272eccbce21b1a9a38b branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-04T20:43:52+01:00 summary: [3.12] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) (GH-120071) (cherry picked from commit 109e1082ea92f89d42cd70f2cc7ca6fba6be9bab) files: M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index d2a392bed1c8a7..38caa56dbc5f3e 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3894,9 +3894,9 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") [email protected](support.is_wasi, "WASI does not have multiprocessing.") def test_multiprocessing_queues(self): # See gh-119819 +import_helper.import_module('_multiprocessing') # will skip test if it's not available cd = copy.deepcopy(self.config_queue_handler) from multiprocessing import Queue as MQ, Manager as MM q1 = MQ() # this can't be pickled ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) (GH-120072)
https://github.com/python/cpython/commit/7edc6bd6276ae857928ee7b0e84817c78292939f commit: 7edc6bd6276ae857928ee7b0e84817c78292939f branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-04T20:50:48+01:00 summary: [3.13] gh-119819: Update test to skip if _multiprocessing is unavailable. (GH-120067) (GH-120072) (cherry picked from commit 109e1082ea92f89d42cd70f2cc7ca6fba6be9bab) files: M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index d3e5ac2be2e21e..0c9a24e58dfd8c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3926,9 +3926,9 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") [email protected](support.is_wasi, "WASI does not have multiprocessing.") def test_multiprocessing_queues(self): # See gh-119819 +import_helper.import_module('_multiprocessing') # will skip test if it's not available cd = copy.deepcopy(self.config_queue_handler) from multiprocessing import Queue as MQ, Manager as MM q1 = MQ() # this can't be pickled ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090)
https://github.com/python/cpython/commit/983efcf15b2503fe0c05d5e03762385967962b33
commit: 983efcf15b2503fe0c05d5e03762385967962b33
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-06-05T07:25:47+01:00
summary:
gh-119819: Update logging configuration to support joinable multiproc…
(GH-120090)
gh-119819: Update logging configuration to support joinable multiprocessing
manager queues.
files:
M Lib/logging/config.py
M Lib/test/test_logging.py
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 0b10bf82b60a36..9de84e527b18ac 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -783,8 +783,10 @@ def configure_handler(self, config):
from multiprocessing.queues import Queue as MPQueue
from multiprocessing import Manager as MM
proxy_queue = MM().Queue()
+proxy_joinable_queue = MM().JoinableQueue()
qspec = config['queue']
-if not isinstance(qspec, (queue.Queue, MPQueue,
type(proxy_queue))):
+if not isinstance(qspec, (queue.Queue, MPQueue,
+ type(proxy_queue),
type(proxy_joinable_queue))):
if isinstance(qspec, str):
q = self.resolve(qspec)
if not callable(q):
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 0c9a24e58dfd8c..ef2d4a621be962 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3928,12 +3928,16 @@ def test_config_queue_handler(self):
def test_multiprocessing_queues(self):
# See gh-119819
-import_helper.import_module('_multiprocessing') # will skip test if
it's not available
+
+# will skip test if it's not available
+import_helper.import_module('_multiprocessing')
+
cd = copy.deepcopy(self.config_queue_handler)
from multiprocessing import Queue as MQ, Manager as MM
q1 = MQ() # this can't be pickled
q2 = MM().Queue() # a proxy queue for use when pickling is needed
-for qspec in (q1, q2):
+q3 = MM().JoinableQueue() # a joinable proxy queue
+for qspec in (q1, q2, q3):
fn = make_temp_file('.log', 'test_logging-cmpqh-')
cd['handlers']['h1']['filename'] = fn
cd['handlers']['ah']['queue'] = qspec
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090) (GH-120092)
https://github.com/python/cpython/commit/8d199774cb6b68b295b78cb8be9a3afc805ac880 commit: 8d199774cb6b68b295b78cb8be9a3afc805ac880 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-05T07:59:15+01:00 summary: [3.12] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090) (GH-120092) (cherry picked from commit 983efcf15b2503fe0c05d5e03762385967962b33) files: M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index c98eb6c8215d1a..1824d0aa747225 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -790,8 +790,10 @@ def configure_handler(self, config): from multiprocessing.queues import Queue as MPQueue from multiprocessing import Manager as MM proxy_queue = MM().Queue() +proxy_joinable_queue = MM().JoinableQueue() qspec = config['queue'] -if not isinstance(qspec, (queue.Queue, MPQueue, type(proxy_queue))): +if not isinstance(qspec, (queue.Queue, MPQueue, + type(proxy_queue), type(proxy_joinable_queue))): if isinstance(qspec, str): q = self.resolve(qspec) if not callable(q): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 38caa56dbc5f3e..6dd1b6f8047020 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3896,12 +3896,16 @@ def test_config_queue_handler(self): def test_multiprocessing_queues(self): # See gh-119819 -import_helper.import_module('_multiprocessing') # will skip test if it's not available + +# will skip test if it's not available +import_helper.import_module('_multiprocessing') + cd = copy.deepcopy(self.config_queue_handler) from multiprocessing import Queue as MQ, Manager as MM q1 = MQ() # this can't be pickled q2 = MM().Queue() # a proxy queue for use when pickling is needed -for qspec in (q1, q2): +q3 = MM().JoinableQueue() # a joinable proxy queue +for qspec in (q1, q2, q3): fn = make_temp_file('.log', 'test_logging-cmpqh-') cd['handlers']['h1']['filename'] = fn cd['handlers']['ah']['queue'] = qspec ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090) (GH-120093)
https://github.com/python/cpython/commit/71f86eedeb29d1933edbc9b27f40ce5cbba2f4a9 commit: 71f86eedeb29d1933edbc9b27f40ce5cbba2f4a9 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-05T07:59:41+01:00 summary: [3.13] gh-119819: Update logging configuration to support joinable multiproc… (GH-120090) (GH-120093) (cherry picked from commit 983efcf15b2503fe0c05d5e03762385967962b33) files: M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 0b10bf82b60a36..9de84e527b18ac 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -783,8 +783,10 @@ def configure_handler(self, config): from multiprocessing.queues import Queue as MPQueue from multiprocessing import Manager as MM proxy_queue = MM().Queue() +proxy_joinable_queue = MM().JoinableQueue() qspec = config['queue'] -if not isinstance(qspec, (queue.Queue, MPQueue, type(proxy_queue))): +if not isinstance(qspec, (queue.Queue, MPQueue, + type(proxy_queue), type(proxy_joinable_queue))): if isinstance(qspec, str): q = self.resolve(qspec) if not callable(q): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 0c9a24e58dfd8c..ef2d4a621be962 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3928,12 +3928,16 @@ def test_config_queue_handler(self): def test_multiprocessing_queues(self): # See gh-119819 -import_helper.import_module('_multiprocessing') # will skip test if it's not available + +# will skip test if it's not available +import_helper.import_module('_multiprocessing') + cd = copy.deepcopy(self.config_queue_handler) from multiprocessing import Queue as MQ, Manager as MM q1 = MQ() # this can't be pickled q2 = MM().Queue() # a proxy queue for use when pickling is needed -for qspec in (q1, q2): +q3 = MM().JoinableQueue() # a joinable proxy queue +for qspec in (q1, q2, q3): fn = make_temp_file('.log', 'test_logging-cmpqh-') cd['handlers']['h1']['filename'] = fn cd['handlers']['ah']['queue'] = qspec ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-120485: Add an override of `allow_reuse_port` on classes subclassing `socketserver.TCPServer` (GH-120488)
https://github.com/python/cpython/commit/192d17c3fd9945104bc0303cf248bb0d074d260e commit: 192d17c3fd9945104bc0303cf248bb0d074d260e branch: main author: Idan Kapustian <[email protected]> committer: vsajip date: 2024-06-16T13:15:03+01:00 summary: gh-120485: Add an override of `allow_reuse_port` on classes subclassing `socketserver.TCPServer` (GH-120488) Co-authored-by: Vinay Sajip files: A Misc/NEWS.d/next/Core and Builtins/2024-06-14-07-52-00.gh-issue-120485.yy4K4b.rst M Lib/http/server.py M Lib/logging/config.py M Lib/test/test_logging.py M Lib/xmlrpc/server.py diff --git a/Lib/http/server.py b/Lib/http/server.py index 7d0da5052d2d4d..2d010649e56b51 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -129,7 +129,8 @@ class HTTPServer(socketserver.TCPServer): -allow_reuse_address = 1# Seems to make sense in testing environment +allow_reuse_address = True# Seems to make sense in testing environment +allow_reuse_port = True def server_bind(self): """Override server_bind to store the server name.""" diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 9de84e527b18ac..d2f23e53f35c57 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -984,7 +984,8 @@ class ConfigSocketReceiver(ThreadingTCPServer): A simple TCP socket-based logging config receiver. """ -allow_reuse_address = 1 +allow_reuse_address = True +allow_reuse_port = True def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT, handler=None, ready=None, verify=None): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 504862ad53395e..5192ce252a4d4c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -1038,6 +1038,7 @@ class TestTCPServer(ControlMixin, ThreadingTCPServer): """ allow_reuse_address = True +allow_reuse_port = True def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True): diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index 4dddb1d10e08bd..90a356fbb8eae4 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -578,6 +578,7 @@ class SimpleXMLRPCServer(socketserver.TCPServer, """ allow_reuse_address = True +allow_reuse_port = True # Warning: this is for debugging purposes only! Never set this to True in # production code, as will be sending out sensitive information (exception diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-06-14-07-52-00.gh-issue-120485.yy4K4b.rst b/Misc/NEWS.d/next/Core and Builtins/2024-06-14-07-52-00.gh-issue-120485.yy4K4b.rst new file mode 100644 index 00..f41c233908362f --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-06-14-07-52-00.gh-issue-120485.yy4K4b.rst @@ -0,0 +1 @@ +Add an override of ``allow_reuse_port`` on classes subclassing ``socketserver.TCPServer`` where ``allow_reuse_address`` is also overridden. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-120868: Fix breaking change in `logging.config` when using `QueueHandler` (GH-120872)
https://github.com/python/cpython/commit/7d9c68513d112823a9a6cdc7453b998b2c24eb4c
commit: 7d9c68513d112823a9a6cdc7453b998b2c24eb4c
branch: main
author: Janek Nouvertné
committer: vsajip
date: 2024-06-27T08:09:01+01:00
summary:
gh-120868: Fix breaking change in `logging.config` when using `QueueHandler`
(GH-120872)
files:
M Lib/logging/config.py
M Lib/test/test_logging.py
M Misc/ACKS
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index d2f23e53f35c57..95e129ae988c24 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -780,25 +780,44 @@ def configure_handler(self, config):
# if 'handlers' not in config:
# raise ValueError('No handlers specified for a
QueueHandler')
if 'queue' in config:
-from multiprocessing.queues import Queue as MPQueue
-from multiprocessing import Manager as MM
-proxy_queue = MM().Queue()
-proxy_joinable_queue = MM().JoinableQueue()
qspec = config['queue']
-if not isinstance(qspec, (queue.Queue, MPQueue,
- type(proxy_queue),
type(proxy_joinable_queue))):
-if isinstance(qspec, str):
-q = self.resolve(qspec)
-if not callable(q):
-raise TypeError('Invalid queue specifier %r' %
qspec)
-q = q()
-elif isinstance(qspec, dict):
-if '()' not in qspec:
-raise TypeError('Invalid queue specifier %r' %
qspec)
-q = self.configure_custom(dict(qspec))
-else:
+
+if isinstance(qspec, str):
+q = self.resolve(qspec)
+if not callable(q):
raise TypeError('Invalid queue specifier %r' %
qspec)
-config['queue'] = q
+config['queue'] = q()
+elif isinstance(qspec, dict):
+if '()' not in qspec:
+raise TypeError('Invalid queue specifier %r' %
qspec)
+config['queue'] = self.configure_custom(dict(qspec))
+else:
+from multiprocessing.queues import Queue as MPQueue
+
+if not isinstance(qspec, (queue.Queue, MPQueue)):
+# Safely check if 'qspec' is an instance of
Manager.Queue
+# / Manager.JoinableQueue
+
+from multiprocessing import Manager as MM
+from multiprocessing.managers import BaseProxy
+
+# if it's not an instance of BaseProxy, it also
can't be
+# an instance of Manager.Queue /
Manager.JoinableQueue
+if isinstance(qspec, BaseProxy):
+# Sometimes manager or queue creation might
fail
+# (e.g. see issue gh-120868). In that case, any
+# exception during the creation of these
queues will
+# propagate up to the caller and be wrapped in
a
+# `ValueError`, whose cause will indicate the
details of
+# the failure.
+mm = MM()
+proxy_queue = mm.Queue()
+proxy_joinable_queue = mm.JoinableQueue()
+if not isinstance(qspec, (type(proxy_queue),
type(proxy_joinable_queue))):
+raise TypeError('Invalid queue specifier
%r' % qspec)
+else:
+raise TypeError('Invalid queue specifier %r' %
qspec)
+
if 'listener' in config:
lspec = config['listener']
if isinstance(lspec, type):
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index ddb9481807bb55..e6daea2333b206 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3928,6 +3928,50 @@ def test_config_queue_handler(self):
msg = str(ctx.exception)
self.assertEqual(msg, "Unable to configure handler 'ah'")
+@threading_helper.requires_working_threading()
[email protected]_subprocess()
+@patch("multiprocessing.Manager")
+def
test_config_queue_handler_does_not_create_multiprocessing_manager(self
[Python-checkins] [3.13] gh-120868: Fix breaking change in `logging.config` when using `QueueHandler` (GH-120872) (GH-121078)
https://github.com/python/cpython/commit/9df7392ebad877fdaa3d6f1c1f1c7504a50efb72 commit: 9df7392ebad877fdaa3d6f1c1f1c7504a50efb72 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-27T10:09:17+01:00 summary: [3.13] gh-120868: Fix breaking change in `logging.config` when using `QueueHandler` (GH-120872) (GH-121078) (cherry picked from commit 7d9c68513d112823a9a6cdc7453b998b2c24eb4c) files: M Lib/logging/config.py M Lib/test/test_logging.py M Misc/ACKS diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 9de84e527b18ac..3cc4c57dd8ef80 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -780,25 +780,44 @@ def configure_handler(self, config): # if 'handlers' not in config: # raise ValueError('No handlers specified for a QueueHandler') if 'queue' in config: -from multiprocessing.queues import Queue as MPQueue -from multiprocessing import Manager as MM -proxy_queue = MM().Queue() -proxy_joinable_queue = MM().JoinableQueue() qspec = config['queue'] -if not isinstance(qspec, (queue.Queue, MPQueue, - type(proxy_queue), type(proxy_joinable_queue))): -if isinstance(qspec, str): -q = self.resolve(qspec) -if not callable(q): -raise TypeError('Invalid queue specifier %r' % qspec) -q = q() -elif isinstance(qspec, dict): -if '()' not in qspec: -raise TypeError('Invalid queue specifier %r' % qspec) -q = self.configure_custom(dict(qspec)) -else: + +if isinstance(qspec, str): +q = self.resolve(qspec) +if not callable(q): raise TypeError('Invalid queue specifier %r' % qspec) -config['queue'] = q +config['queue'] = q() +elif isinstance(qspec, dict): +if '()' not in qspec: +raise TypeError('Invalid queue specifier %r' % qspec) +config['queue'] = self.configure_custom(dict(qspec)) +else: +from multiprocessing.queues import Queue as MPQueue + +if not isinstance(qspec, (queue.Queue, MPQueue)): +# Safely check if 'qspec' is an instance of Manager.Queue +# / Manager.JoinableQueue + +from multiprocessing import Manager as MM +from multiprocessing.managers import BaseProxy + +# if it's not an instance of BaseProxy, it also can't be +# an instance of Manager.Queue / Manager.JoinableQueue +if isinstance(qspec, BaseProxy): +# Sometimes manager or queue creation might fail +# (e.g. see issue gh-120868). In that case, any +# exception during the creation of these queues will +# propagate up to the caller and be wrapped in a +# `ValueError`, whose cause will indicate the details of +# the failure. +mm = MM() +proxy_queue = mm.Queue() +proxy_joinable_queue = mm.JoinableQueue() +if not isinstance(qspec, (type(proxy_queue), type(proxy_joinable_queue))): +raise TypeError('Invalid queue specifier %r' % qspec) +else: +raise TypeError('Invalid queue specifier %r' % qspec) + if 'listener' in config: lspec = config['listener'] if isinstance(lspec, type): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index c78e76d16ae5cf..4223d10e79117c 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3927,6 +3927,50 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") +@threading_helper.requires_working_threading() [email protected]_
[Python-checkins] gh-121035: Update logging flow chart to include the lastResort handler. (GH-121036)
https://github.com/python/cpython/commit/237baf4d7a789deb153fbc1fc3863550949d5da2
commit: 237baf4d7a789deb153fbc1fc3863550949d5da2
branch: main
author: Alexander Bessman
committer: vsajip
date: 2024-06-27T22:11:40+01:00
summary:
gh-121035: Update logging flow chart to include the lastResort handler.
(GH-121036)
files:
A Doc/howto/logging_flow.svg
M Doc/howto/logging.rst
M Doc/howto/logging_flow.png
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index cf5b693d8e3851..316b16aa796af4 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -382,8 +382,8 @@ Logging Flow
The flow of log event information in loggers and handlers is illustrated in the
following diagram.
-.. image:: logging_flow.png
- :class: invert-in-dark-mode
+.. raw:: html
+ :file: logging_flow.svg
Loggers
^^^
diff --git a/Doc/howto/logging_flow.png b/Doc/howto/logging_flow.png
index d65e597f811db5..c2d0befe27326c 100644
Binary files a/Doc/howto/logging_flow.png and b/Doc/howto/logging_flow.png
differ
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
new file mode 100644
index 00..52206bdbcf503b
--- /dev/null
+++ b/Doc/howto/logging_flow.svg
@@ -0,0 +1,281 @@
+
+http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd";>
+http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
+
+
+
+ @media (prefers-color-scheme: dark)
+ {
+svg {
+ filter: invert(93%) hue-rotate(180deg);
+ background-color: transparent !important;
+}
+image {
+ filter: invert(100%) hue-rotate(180deg) saturate(1.25);
+}
+ }
+
+
+
+
+
+
+
+
+ Logger flow
+
+
+
+
+Create
+LogRecord
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Logging call in user
+ code, e.g.
+
+
+ logging.info(...)
+
+
+
+
+
+
+
+
+Stop
+
+
+
+
+
+Does a filter attached
+to logger reject the
+record?
+
+
+
+
+
+
+
+
+
+Pass to
+handlers of
+current logger
+
+
+
+
+
+Is propagate true for
+current logger?
+
+
+
+
+
+Is there a parent
+logger?
+
+
+
+
+
+Set current
+logger to parent
+
+
+
+
+
+At least one handler
+in hierarchy?
+
+
+
+
+
+Use lastResort
+handler
+
+
+
+
+
+Handler enabled for
+level of LogRecord?
+
+
+
+
+
+Does a filter attached
+to handler reject the
+record?
+
+
+
+
+
+Stop
+
+
+
+
+
+Emit (includes formatting)
+
+
+
+ Handler flow
+
+
+
+
+Logger enabled for
+level of call?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No
+
+
+ Yes
+
+
+ Yes
+
+
+ No
+
+
+ No
+
+
+ Yes
+
+
+ Yes
+
+
+ No
+
+
+ No
+
+
+ Yes
+
+
+
+
+
+ No
+
+
+
+
+
+
+
+
+ Yes
+
+
+ No
+
+
+
+
+
+ Yes
+
+
+ LogRecord passed
+ to handler
+
+
+
+
+
+
+
+
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.13] gh-121035: Update logging flow chart to include the lastResort handler. (GH-121036) (GH-121106)
https://github.com/python/cpython/commit/49a01d6290631ef65da6cd142df41c72a7dae3b5 commit: 49a01d6290631ef65da6cd142df41c72a7dae3b5 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-27T22:59:19+01:00 summary: [3.13] gh-121035: Update logging flow chart to include the lastResort handler. (GH-121036) (GH-121106) (cherry picked from commit 237baf4d7a789deb153fbc1fc3863550949d5da2) files: A Doc/howto/logging_flow.svg M Doc/howto/logging.rst M Doc/howto/logging_flow.png diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index cf5b693d8e3851..316b16aa796af4 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -382,8 +382,8 @@ Logging Flow The flow of log event information in loggers and handlers is illustrated in the following diagram. -.. image:: logging_flow.png - :class: invert-in-dark-mode +.. raw:: html + :file: logging_flow.svg Loggers ^^^ diff --git a/Doc/howto/logging_flow.png b/Doc/howto/logging_flow.png index d65e597f811db5..c2d0befe27326c 100644 Binary files a/Doc/howto/logging_flow.png and b/Doc/howto/logging_flow.png differ diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg new file mode 100644 index 00..52206bdbcf503b --- /dev/null +++ b/Doc/howto/logging_flow.svg @@ -0,0 +1,281 @@ + +http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd";> +http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";> + + + + @media (prefers-color-scheme: dark) + { +svg { + filter: invert(93%) hue-rotate(180deg); + background-color: transparent !important; +} +image { + filter: invert(100%) hue-rotate(180deg) saturate(1.25); +} + } + + + + + + + + + Logger flow + + + + +Create +LogRecord + + + + + + + + + + + + + + + + + + Logging call in user + code, e.g. + + + logging.info(...) + + + + + + + + +Stop + + + + + +Does a filter attached +to logger reject the +record? + + + + + + + + + +Pass to +handlers of +current logger + + + + + +Is propagate true for +current logger? + + + + + +Is there a parent +logger? + + + + + +Set current +logger to parent + + + + + +At least one handler +in hierarchy? + + + + + +Use lastResort +handler + + + + + +Handler enabled for +level of LogRecord? + + + + + +Does a filter attached +to handler reject the +record? + + + + + +Stop + + + + + +Emit (includes formatting) + + + + Handler flow + + + + +Logger enabled for +level of call? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No + + + Yes + + + Yes + + + No + + + No + + + Yes + + + Yes + + + No + + + No + + + Yes + + + + + + No + + + + + + + + + Yes + + + No + + + + + + Yes + + + LogRecord passed + to handler + + + + + + + + ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-121035: Update logging flow chart to include the lastResort handler. (GH-121036) (GH-121105)
https://github.com/python/cpython/commit/a5048ad990bab80ecfba9a9d4557100fc19b37f4 commit: a5048ad990bab80ecfba9a9d4557100fc19b37f4 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-27T23:20:36+01:00 summary: [3.12] gh-121035: Update logging flow chart to include the lastResort handler. (GH-121036) (GH-121105) (cherry picked from commit 237baf4d7a789deb153fbc1fc3863550949d5da2) files: A Doc/howto/logging_flow.svg M Doc/howto/logging.rst M Doc/howto/logging_flow.png diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 877cb24328c160..a88ba00e7b2f58 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -381,8 +381,8 @@ Logging Flow The flow of log event information in loggers and handlers is illustrated in the following diagram. -.. image:: logging_flow.png - :class: invert-in-dark-mode +.. raw:: html + :file: logging_flow.svg Loggers ^^^ diff --git a/Doc/howto/logging_flow.png b/Doc/howto/logging_flow.png index d65e597f811db5..c2d0befe27326c 100644 Binary files a/Doc/howto/logging_flow.png and b/Doc/howto/logging_flow.png differ diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg new file mode 100644 index 00..52206bdbcf503b --- /dev/null +++ b/Doc/howto/logging_flow.svg @@ -0,0 +1,281 @@ + +http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd";> +http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";> + + + + @media (prefers-color-scheme: dark) + { +svg { + filter: invert(93%) hue-rotate(180deg); + background-color: transparent !important; +} +image { + filter: invert(100%) hue-rotate(180deg) saturate(1.25); +} + } + + + + + + + + + Logger flow + + + + +Create +LogRecord + + + + + + + + + + + + + + + + + + Logging call in user + code, e.g. + + + logging.info(...) + + + + + + + + +Stop + + + + + +Does a filter attached +to logger reject the +record? + + + + + + + + + +Pass to +handlers of +current logger + + + + + +Is propagate true for +current logger? + + + + + +Is there a parent +logger? + + + + + +Set current +logger to parent + + + + + +At least one handler +in hierarchy? + + + + + +Use lastResort +handler + + + + + +Handler enabled for +level of LogRecord? + + + + + +Does a filter attached +to handler reject the +record? + + + + + +Stop + + + + + +Emit (includes formatting) + + + + Handler flow + + + + +Logger enabled for +level of call? + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No + + + Yes + + + Yes + + + No + + + No + + + Yes + + + Yes + + + No + + + No + + + Yes + + + + + + No + + + + + + + + + Yes + + + No + + + + + + Yes + + + LogRecord passed + to handler + + + + + + + + ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-120868: Fix breaking change in `logging.config` when using `QueueHandler` (GH-120872) (GH-121077)
https://github.com/python/cpython/commit/b31f7e2e90a811f46f1934dd3604412d1381eede commit: b31f7e2e90a811f46f1934dd3604412d1381eede branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-28T17:10:53+01:00 summary: [3.12] gh-120868: Fix breaking change in `logging.config` when using `QueueHandler` (GH-120872) (GH-121077) (cherry picked from commit 7d9c68513d112823a9a6cdc7453b998b2c24eb4c) files: M Lib/logging/config.py M Lib/test/test_logging.py M Misc/ACKS diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 1824d0aa747225..1b59876a955a0b 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -787,25 +787,44 @@ def configure_handler(self, config): # if 'handlers' not in config: # raise ValueError('No handlers specified for a QueueHandler') if 'queue' in config: -from multiprocessing.queues import Queue as MPQueue -from multiprocessing import Manager as MM -proxy_queue = MM().Queue() -proxy_joinable_queue = MM().JoinableQueue() qspec = config['queue'] -if not isinstance(qspec, (queue.Queue, MPQueue, - type(proxy_queue), type(proxy_joinable_queue))): -if isinstance(qspec, str): -q = self.resolve(qspec) -if not callable(q): -raise TypeError('Invalid queue specifier %r' % qspec) -q = q() -elif isinstance(qspec, dict): -if '()' not in qspec: -raise TypeError('Invalid queue specifier %r' % qspec) -q = self.configure_custom(dict(qspec)) -else: + +if isinstance(qspec, str): +q = self.resolve(qspec) +if not callable(q): raise TypeError('Invalid queue specifier %r' % qspec) -config['queue'] = q +config['queue'] = q() +elif isinstance(qspec, dict): +if '()' not in qspec: +raise TypeError('Invalid queue specifier %r' % qspec) +config['queue'] = self.configure_custom(dict(qspec)) +else: +from multiprocessing.queues import Queue as MPQueue + +if not isinstance(qspec, (queue.Queue, MPQueue)): +# Safely check if 'qspec' is an instance of Manager.Queue +# / Manager.JoinableQueue + +from multiprocessing import Manager as MM +from multiprocessing.managers import BaseProxy + +# if it's not an instance of BaseProxy, it also can't be +# an instance of Manager.Queue / Manager.JoinableQueue +if isinstance(qspec, BaseProxy): +# Sometimes manager or queue creation might fail +# (e.g. see issue gh-120868). In that case, any +# exception during the creation of these queues will +# propagate up to the caller and be wrapped in a +# `ValueError`, whose cause will indicate the details of +# the failure. +mm = MM() +proxy_queue = mm.Queue() +proxy_joinable_queue = mm.JoinableQueue() +if not isinstance(qspec, (type(proxy_queue), type(proxy_joinable_queue))): +raise TypeError('Invalid queue specifier %r' % qspec) +else: +raise TypeError('Invalid queue specifier %r' % qspec) + if 'listener' in config: lspec = config['listener'] if isinstance(lspec, type): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index a51f60a0b18e3e..951c08a6e4b50e 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -60,6 +60,7 @@ import weakref from http.server import HTTPServer, BaseHTTPRequestHandler +from unittest.mock import patch from urllib.parse import urlparse, parse_qs from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
[Python-checkins] [doc] Update element positions and styles in logging flow diagram. (GH-121182)
https://github.com/python/cpython/commit/2a455bbe8fd91a688ae20509a2fdc8beaa8c8447
commit: 2a455bbe8fd91a688ae20509a2fdc8beaa8c8447
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-06-30T14:38:49+01:00
summary:
[doc] Update element positions and styles in logging flow diagram. (GH-121182)
Update element positions and styles.
files:
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index 52206bdbcf503b..a5f656b1df0b42 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -1,281 +1,304 @@
-
-http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd";>
http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
-
- @media (prefers-color-scheme: dark)
- {
-svg {
- filter: invert(93%) hue-rotate(180deg);
- background-color: transparent !important;
+ svg {
+background-color: transparent !important;
+ }
+line {
+ stroke: #00;
+ fill: none;
+ stroke-opacity: 1;
+}
+polygon, rect {
+ fill: none;
+ stroke: #00;
+ fill-opacity: 1;
+ stroke-opacity: 1;
+}
+polygon.filled {
+ fill: #ff;
+}
+polyline {
+ fill: none;
+ stroke-opacity: 1;
+ stroke: #00;
}
-image {
- filter: invert(100%) hue-rotate(180deg) saturate(1.25);
+text {
+ fill: #00;
+ fill-opacity: 1;
+ stroke: none;
+ font-family: sans-serif;
+ font-style: normal;
+ font-weight: normal;
+ text-anchor: start;
+}
+@media (prefers-color-scheme: dark) {
+ polygon, rect, polyline, line {
+stroke: #ff;
+ }
+ text {
+fill: #ff;
+ }
+ image {
+filter: invert(100%) hue-rotate(180deg) saturate(1.25);
+ }
}
- }
-
+
-
-
-
-
+
+
+
+
Logger flow
-
-
+
+
Create
LogRecord
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
- Logging call in user
- code, e.g.
+
+ Logging call in user
+ code, e.g.
-
- logging.info(...)
+
+ logger.info(...)
-
-
+
+
-
-
+
+
Stop
-
-
-Does a filter attached
-to logger reject the
-record?
+
+
+Does a filter attached
+to logger reject the
+record?
-
-
+
+
-
-
+
+
Pass to
handlers of
current logger
-
-
-Is propagate true for
-current logger?
+
+
+Is propagate true for
+current logger?
-
-
-Is there a parent
-logger?
+
+
+Is there a parent
+logger?
-
-
+
+
Set current
logger to parent
-
-
-At least one handler
-in hierarchy?
+
+
+At least one handler
+in hierarchy?
-
-
-Use lastResort
-handler
+
+
+Use lastResort
+handler
-
-
+
+
Handler enabled for
level of LogRecord?
-
-
-Does a filter attached
-to handler reject the
-record?
+
+
+Does a filter attached
+to handler reject the
+record?
-
-
+
+
Stop
-
-
+
+
Emit (includes formatting)
-
+
Handler flow
-
-
-Logger enabled for
-level of call?
+
+
+Logger enabled for
+level of call?
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
No
-
+
Yes
-
+
Yes
-
+
No
-
+
No
[Python-checkins] [3.12] [doc] Update element positions and styles in logging flow diagram. (GH-121182) (GH-121184)
https://github.com/python/cpython/commit/99bc8589f09e66682a52df1f1a9598c7056d49dd commit: 99bc8589f09e66682a52df1f1a9598c7056d49dd branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-30T14:53:10+01:00 summary: [3.12] [doc] Update element positions and styles in logging flow diagram. (GH-121182) (GH-121184) (cherry picked from commit 2a455bbe8fd91a688ae20509a2fdc8beaa8c8447) files: M Doc/howto/logging_flow.svg diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg index 52206bdbcf503b..a5f656b1df0b42 100644 --- a/Doc/howto/logging_flow.svg +++ b/Doc/howto/logging_flow.svg @@ -1,281 +1,304 @@ - -http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd";> http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";> - - @media (prefers-color-scheme: dark) - { -svg { - filter: invert(93%) hue-rotate(180deg); - background-color: transparent !important; + svg { +background-color: transparent !important; + } +line { + stroke: #00; + fill: none; + stroke-opacity: 1; +} +polygon, rect { + fill: none; + stroke: #00; + fill-opacity: 1; + stroke-opacity: 1; +} +polygon.filled { + fill: #ff; +} +polyline { + fill: none; + stroke-opacity: 1; + stroke: #00; } -image { - filter: invert(100%) hue-rotate(180deg) saturate(1.25); +text { + fill: #00; + fill-opacity: 1; + stroke: none; + font-family: sans-serif; + font-style: normal; + font-weight: normal; + text-anchor: start; +} +@media (prefers-color-scheme: dark) { + polygon, rect, polyline, line { +stroke: #ff; + } + text { +fill: #ff; + } + image { +filter: invert(100%) hue-rotate(180deg) saturate(1.25); + } } - } - + - - - - + + + + Logger flow - - + + Create LogRecord - - + + - - + + - - - - - - - - Logging call in user - code, e.g. + + Logging call in user + code, e.g. - - logging.info(...) + + logger.info(...) - - + + - - + + Stop - - -Does a filter attached -to logger reject the -record? + + +Does a filter attached +to logger reject the +record? - - + + - - + + Pass to handlers of current logger - - -Is propagate true for -current logger? + + +Is propagate true for +current logger? - - -Is there a parent -logger? + + +Is there a parent +logger? - - + + Set current logger to parent - - -At least one handler -in hierarchy? + + +At least one handler +in hierarchy? - - -Use lastResort -handler + + +Use lastResort +handler - - + + Handler enabled for level of LogRecord? - - -Does a filter attached -to handler reject the -record? + + +Does a filter attached +to handler reject the +record? - - + + Stop - - + + Emit (includes formatting) - + Handler flow - - -Logger enabled for -level of call? + + +Logger enabled for +level of call? - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - +
[Python-checkins] [3.13] [doc] Update element positions and styles in logging flow diagram. (GH-121182) (GH-121183)
https://github.com/python/cpython/commit/af89237c9c1bdcda7915ea195b279734f7ebddf2 commit: af89237c9c1bdcda7915ea195b279734f7ebddf2 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-06-30T15:06:27+01:00 summary: [3.13] [doc] Update element positions and styles in logging flow diagram. (GH-121182) (GH-121183) (cherry picked from commit 2a455bbe8fd91a688ae20509a2fdc8beaa8c8447) files: M Doc/howto/logging_flow.svg diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg index 52206bdbcf503b..a5f656b1df0b42 100644 --- a/Doc/howto/logging_flow.svg +++ b/Doc/howto/logging_flow.svg @@ -1,281 +1,304 @@ - -http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd";> http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";> - - @media (prefers-color-scheme: dark) - { -svg { - filter: invert(93%) hue-rotate(180deg); - background-color: transparent !important; + svg { +background-color: transparent !important; + } +line { + stroke: #00; + fill: none; + stroke-opacity: 1; +} +polygon, rect { + fill: none; + stroke: #00; + fill-opacity: 1; + stroke-opacity: 1; +} +polygon.filled { + fill: #ff; +} +polyline { + fill: none; + stroke-opacity: 1; + stroke: #00; } -image { - filter: invert(100%) hue-rotate(180deg) saturate(1.25); +text { + fill: #00; + fill-opacity: 1; + stroke: none; + font-family: sans-serif; + font-style: normal; + font-weight: normal; + text-anchor: start; +} +@media (prefers-color-scheme: dark) { + polygon, rect, polyline, line { +stroke: #ff; + } + text { +fill: #ff; + } + image { +filter: invert(100%) hue-rotate(180deg) saturate(1.25); + } } - } - + - - - - + + + + Logger flow - - + + Create LogRecord - - + + - - + + - - - - - - - - Logging call in user - code, e.g. + + Logging call in user + code, e.g. - - logging.info(...) + + logger.info(...) - - + + - - + + Stop - - -Does a filter attached -to logger reject the -record? + + +Does a filter attached +to logger reject the +record? - - + + - - + + Pass to handlers of current logger - - -Is propagate true for -current logger? + + +Is propagate true for +current logger? - - -Is there a parent -logger? + + +Is there a parent +logger? - - + + Set current logger to parent - - -At least one handler -in hierarchy? + + +At least one handler +in hierarchy? - - -Use lastResort -handler + + +Use lastResort +handler - - + + Handler enabled for level of LogRecord? - - -Does a filter attached -to handler reject the -record? + + +Does a filter attached +to handler reject the +record? - - + + Stop - - + + Emit (includes formatting) - + Handler flow - - -Logger enabled for -level of call? + + +Logger enabled for +level of call? - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - +
[Python-checkins] gh-121035: Improve logging flow diagram for dark/light modes. (GH-121254)
https://github.com/python/cpython/commit/bfe0e4d7696647a546110328510bdb98146ad2f2
commit: bfe0e4d7696647a546110328510bdb98146ad2f2
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-07-02T09:13:37+01:00
summary:
gh-121035: Improve logging flow diagram for dark/light modes. (GH-121254)
files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 316b16aa796af4..9c55233e910f17 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -385,6 +385,44 @@ following diagram.
.. raw:: html
:file: logging_flow.svg
+.. raw:: html
+
+
+ /*
+* This snippet is needed to handle the case where a light or dark theme is
+* chosen via the theme is selected in the page. We call the existing
handler
+* and then add a dark-theme class to the body when the dark theme is
selected.
+* The SVG styling (above) then does the rest.
+*
+* If the pydoc theme is updated to set the dark-theme class, this snippet
+* won't be needed any more.
+*/
+ (function() {
+ var oldActivateTheme = activateTheme;
+
+ function updateBody(theme) {
+let elem = document.body;
+
+if (theme === 'dark') {
+elem.classList.add('dark-theme');
+}
+else {
+elem.classList.remove('dark-theme');
+}
+ }
+
+ activateTheme = function(theme) {
+oldActivateTheme(theme);
+updateBody(theme);
+ };
+ /*
+ * If the page is refreshed, make sure we update the body - the overriding
+ * of activateTheme won't have taken effect yet.
+ */
+ updateBody(localStorage.getItem('currentTheme') || 'auto');
+ })();
+
+
Loggers
^^^
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index a5f656b1df0b42..9807323b7190d6 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -1,9 +1,9 @@
http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
- svg {
-background-color: transparent !important;
- }
+svg {
+ background-color: transparent !important;
+}
line {
stroke: #00;
fill: none;
@@ -16,7 +16,7 @@
stroke-opacity: 1;
}
polygon.filled {
- fill: #ff;
+ fill: #00;
}
polyline {
fill: none;
@@ -36,6 +36,9 @@
polygon, rect, polyline, line {
stroke: #ff;
}
+ polygon.filled {
+fill: #ff;
+ }
text {
fill: #ff;
}
@@ -43,6 +46,15 @@
filter: invert(100%) hue-rotate(180deg) saturate(1.25);
}
}
+body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline,
body.dark-theme line {
+ stroke: #ff;
+}
+body.dark-theme polygon.filled {
+ fill: #ff;
+}
+body.dark-theme text {
+ fill: #ff;
+}
@@ -57,7 +69,7 @@
Create
-LogRecord
+LogRecord
@@ -100,7 +112,7 @@
-Pass to
+Pass record to
handlers of
current logger
@@ -135,16 +147,17 @@
-
-Use lastResort
-handler
+
+Use
+lastResort
+handler
Handler enabled for
-level of LogRecord?
+level of record?
@@ -292,7 +305,7 @@
Yes
- LogRecord passed
+ Record passed
to handler
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] gh-121035: Further improve logging flow diagram with respect to dark/light modes. (GH-121265)
https://github.com/python/cpython/commit/089835469d5efbea4793cd611b43cb8387f2e7e5
commit: 089835469d5efbea4793cd611b43cb8387f2e7e5
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-07-02T18:57:34+01:00
summary:
gh-121035: Further improve logging flow diagram with respect to dark/light
modes. (GH-121265)
files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 9c55233e910f17..cbfe93319ddaa4 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -403,11 +403,13 @@ following diagram.
function updateBody(theme) {
let elem = document.body;
+elem.classList.remove('dark-theme');
+elem.classList.remove('light-theme');
if (theme === 'dark') {
elem.classList.add('dark-theme');
}
-else {
-elem.classList.remove('dark-theme');
+else if (theme === 'light') {
+elem.classList.add('light-theme');
}
}
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index 9807323b7190d6..4974994ac6b400 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -46,6 +46,7 @@
filter: invert(100%) hue-rotate(180deg) saturate(1.25);
}
}
+/* These rules are for when the theme selector is used, perhaps in
contrast to the browser theme. */
body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline,
body.dark-theme line {
stroke: #ff;
}
@@ -55,6 +56,15 @@
body.dark-theme text {
fill: #ff;
}
+body.light-theme polygon, body.light-theme rect, body.light-theme
polyline, body.light-theme line {
+ stroke: #00;
+}
+body.light-theme polygon.filled {
+ fill: #00;
+}
+body.light-theme text {
+ fill: #00;
+}
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-121035: Improve logging flow diagram for dark/light modes. (GH-121254) (GH-121316)
https://github.com/python/cpython/commit/c3f393c8adb08eaa5ef68e4eece279d809f2a717
commit: c3f393c8adb08eaa5ef68e4eece279d809f2a717
branch: 3.12
author: Vinay Sajip
committer: vsajip
date: 2024-07-03T10:48:34+01:00
summary:
[3.12] gh-121035: Improve logging flow diagram for dark/light modes.
(GH-121254) (GH-121316)
(cherry picked from commit bfe0e4d7696647a546110328510bdb98146ad2f2)
files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index a88ba00e7b2f58..3f3cb55cfe9035 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -384,6 +384,44 @@ following diagram.
.. raw:: html
:file: logging_flow.svg
+.. raw:: html
+
+
+ /*
+* This snippet is needed to handle the case where a light or dark theme is
+* chosen via the theme is selected in the page. We call the existing
handler
+* and then add a dark-theme class to the body when the dark theme is
selected.
+* The SVG styling (above) then does the rest.
+*
+* If the pydoc theme is updated to set the dark-theme class, this snippet
+* won't be needed any more.
+*/
+ (function() {
+ var oldActivateTheme = activateTheme;
+
+ function updateBody(theme) {
+let elem = document.body;
+
+if (theme === 'dark') {
+elem.classList.add('dark-theme');
+}
+else {
+elem.classList.remove('dark-theme');
+}
+ }
+
+ activateTheme = function(theme) {
+oldActivateTheme(theme);
+updateBody(theme);
+ };
+ /*
+ * If the page is refreshed, make sure we update the body - the overriding
+ * of activateTheme won't have taken effect yet.
+ */
+ updateBody(localStorage.getItem('currentTheme') || 'auto');
+ })();
+
+
Loggers
^^^
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index a5f656b1df0b42..9807323b7190d6 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -1,9 +1,9 @@
http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
- svg {
-background-color: transparent !important;
- }
+svg {
+ background-color: transparent !important;
+}
line {
stroke: #00;
fill: none;
@@ -16,7 +16,7 @@
stroke-opacity: 1;
}
polygon.filled {
- fill: #ff;
+ fill: #00;
}
polyline {
fill: none;
@@ -36,6 +36,9 @@
polygon, rect, polyline, line {
stroke: #ff;
}
+ polygon.filled {
+fill: #ff;
+ }
text {
fill: #ff;
}
@@ -43,6 +46,15 @@
filter: invert(100%) hue-rotate(180deg) saturate(1.25);
}
}
+body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline,
body.dark-theme line {
+ stroke: #ff;
+}
+body.dark-theme polygon.filled {
+ fill: #ff;
+}
+body.dark-theme text {
+ fill: #ff;
+}
@@ -57,7 +69,7 @@
Create
-LogRecord
+LogRecord
@@ -100,7 +112,7 @@
-Pass to
+Pass record to
handlers of
current logger
@@ -135,16 +147,17 @@
-
-Use lastResort
-handler
+
+Use
+lastResort
+handler
Handler enabled for
-level of LogRecord?
+level of record?
@@ -292,7 +305,7 @@
Yes
- LogRecord passed
+ Record passed
to handler
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.13] gh-121035: Improve logging flow diagram for dark/light modes. (GH-121254) (GH-121317)
https://github.com/python/cpython/commit/b62605d50fe1b123e9e9bd19a3230c6dc817ac79
commit: b62605d50fe1b123e9e9bd19a3230c6dc817ac79
branch: 3.13
author: Vinay Sajip
committer: vsajip
date: 2024-07-03T11:01:50+01:00
summary:
[3.13] gh-121035: Improve logging flow diagram for dark/light modes.
(GH-121254) (GH-121317)
(cherry picked from commit bfe0e4d7696647a546110328510bdb98146ad2f2)
files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 316b16aa796af4..9c55233e910f17 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -385,6 +385,44 @@ following diagram.
.. raw:: html
:file: logging_flow.svg
+.. raw:: html
+
+
+ /*
+* This snippet is needed to handle the case where a light or dark theme is
+* chosen via the theme is selected in the page. We call the existing
handler
+* and then add a dark-theme class to the body when the dark theme is
selected.
+* The SVG styling (above) then does the rest.
+*
+* If the pydoc theme is updated to set the dark-theme class, this snippet
+* won't be needed any more.
+*/
+ (function() {
+ var oldActivateTheme = activateTheme;
+
+ function updateBody(theme) {
+let elem = document.body;
+
+if (theme === 'dark') {
+elem.classList.add('dark-theme');
+}
+else {
+elem.classList.remove('dark-theme');
+}
+ }
+
+ activateTheme = function(theme) {
+oldActivateTheme(theme);
+updateBody(theme);
+ };
+ /*
+ * If the page is refreshed, make sure we update the body - the overriding
+ * of activateTheme won't have taken effect yet.
+ */
+ updateBody(localStorage.getItem('currentTheme') || 'auto');
+ })();
+
+
Loggers
^^^
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index a5f656b1df0b42..9807323b7190d6 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -1,9 +1,9 @@
http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";>
- svg {
-background-color: transparent !important;
- }
+svg {
+ background-color: transparent !important;
+}
line {
stroke: #00;
fill: none;
@@ -16,7 +16,7 @@
stroke-opacity: 1;
}
polygon.filled {
- fill: #ff;
+ fill: #00;
}
polyline {
fill: none;
@@ -36,6 +36,9 @@
polygon, rect, polyline, line {
stroke: #ff;
}
+ polygon.filled {
+fill: #ff;
+ }
text {
fill: #ff;
}
@@ -43,6 +46,15 @@
filter: invert(100%) hue-rotate(180deg) saturate(1.25);
}
}
+body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline,
body.dark-theme line {
+ stroke: #ff;
+}
+body.dark-theme polygon.filled {
+ fill: #ff;
+}
+body.dark-theme text {
+ fill: #ff;
+}
@@ -57,7 +69,7 @@
Create
-LogRecord
+LogRecord
@@ -100,7 +112,7 @@
-Pass to
+Pass record to
handlers of
current logger
@@ -135,16 +147,17 @@
-
-Use lastResort
-handler
+
+Use
+lastResort
+handler
Handler enabled for
-level of LogRecord?
+level of record?
@@ -292,7 +305,7 @@
Yes
- LogRecord passed
+ Record passed
to handler
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-121035: Further improve logging flow diagram with respect to dark/light modes. (GH-121265) (GH-121321)
https://github.com/python/cpython/commit/10818024c876c17d6ce1ebfaaa88e02607abb2f6
commit: 10818024c876c17d6ce1ebfaaa88e02607abb2f6
branch: 3.12
author: Vinay Sajip
committer: vsajip
date: 2024-07-03T11:40:26+01:00
summary:
[3.12] gh-121035: Further improve logging flow diagram with respect to
dark/light modes. (GH-121265) (GH-121321)
(cherry picked from commit 089835469d5efbea4793cd611b43cb8387f2e7e5)
files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 3f3cb55cfe9035..b96ff7fd20ee20 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -402,11 +402,13 @@ following diagram.
function updateBody(theme) {
let elem = document.body;
+elem.classList.remove('dark-theme');
+elem.classList.remove('light-theme');
if (theme === 'dark') {
elem.classList.add('dark-theme');
}
-else {
-elem.classList.remove('dark-theme');
+else if (theme === 'light') {
+elem.classList.add('light-theme');
}
}
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index 9807323b7190d6..4974994ac6b400 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -46,6 +46,7 @@
filter: invert(100%) hue-rotate(180deg) saturate(1.25);
}
}
+/* These rules are for when the theme selector is used, perhaps in
contrast to the browser theme. */
body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline,
body.dark-theme line {
stroke: #ff;
}
@@ -55,6 +56,15 @@
body.dark-theme text {
fill: #ff;
}
+body.light-theme polygon, body.light-theme rect, body.light-theme
polyline, body.light-theme line {
+ stroke: #00;
+}
+body.light-theme polygon.filled {
+ fill: #00;
+}
+body.light-theme text {
+ fill: #00;
+}
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.13] gh-121035: Further improve logging flow diagram with respect to dark/light modes. (GH-121265) (GH-121320)
https://github.com/python/cpython/commit/3302c5f1552233fc25dffc780355afe946b58ee3
commit: 3302c5f1552233fc25dffc780355afe946b58ee3
branch: 3.13
author: Vinay Sajip
committer: vsajip
date: 2024-07-03T11:41:18+01:00
summary:
[3.13] gh-121035: Further improve logging flow diagram with respect to
dark/light modes. (GH-121265) (GH-121320)
[3.13] gh-121035: Further improve logging flow diagram with respect to
dark/light modes. (GH-121265)
(cherry picked from commit 089835469d5efbea4793cd611b43cb8387f2e7e5)
files:
M Doc/howto/logging.rst
M Doc/howto/logging_flow.svg
diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
index 9c55233e910f17..cbfe93319ddaa4 100644
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -403,11 +403,13 @@ following diagram.
function updateBody(theme) {
let elem = document.body;
+elem.classList.remove('dark-theme');
+elem.classList.remove('light-theme');
if (theme === 'dark') {
elem.classList.add('dark-theme');
}
-else {
-elem.classList.remove('dark-theme');
+else if (theme === 'light') {
+elem.classList.add('light-theme');
}
}
diff --git a/Doc/howto/logging_flow.svg b/Doc/howto/logging_flow.svg
index 9807323b7190d6..4974994ac6b400 100644
--- a/Doc/howto/logging_flow.svg
+++ b/Doc/howto/logging_flow.svg
@@ -46,6 +46,7 @@
filter: invert(100%) hue-rotate(180deg) saturate(1.25);
}
}
+/* These rules are for when the theme selector is used, perhaps in
contrast to the browser theme. */
body.dark-theme polygon, body.dark-theme rect, body.dark-theme polyline,
body.dark-theme line {
stroke: #ff;
}
@@ -55,6 +56,15 @@
body.dark-theme text {
fill: #ff;
}
+body.light-theme polygon, body.light-theme rect, body.light-theme
polyline, body.light-theme line {
+ stroke: #00;
+}
+body.light-theme polygon.filled {
+ fill: #00;
+}
+body.light-theme text {
+ fill: #00;
+}
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] gh-121035: Update PNG image for logging flow diagram. (GH-121323)
https://github.com/python/cpython/commit/26d24eeb90d781e381b97d64b4dcb1ee4dd891fe commit: 26d24eeb90d781e381b97d64b4dcb1ee4dd891fe branch: main author: Vinay Sajip committer: vsajip date: 2024-07-03T12:33:28+01:00 summary: gh-121035: Update PNG image for logging flow diagram. (GH-121323) files: M Doc/howto/logging_flow.png diff --git a/Doc/howto/logging_flow.png b/Doc/howto/logging_flow.png index c2d0befe27326c..d60ed7c031585a 100644 Binary files a/Doc/howto/logging_flow.png and b/Doc/howto/logging_flow.png differ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-121035: Update PNG image for logging flow diagram. (GH-121323) (GH-121324)
https://github.com/python/cpython/commit/253c033251052dbc6e2de41f5d3840b146c0889b commit: 253c033251052dbc6e2de41f5d3840b146c0889b branch: 3.13 author: Vinay Sajip committer: vsajip date: 2024-07-03T13:01:34+01:00 summary: [3.13] gh-121035: Update PNG image for logging flow diagram. (GH-121323) (GH-121324) (cherry picked from commit 26d24eeb90d781e381b97d64b4dcb1ee4dd891fe) files: M Doc/howto/logging_flow.png diff --git a/Doc/howto/logging_flow.png b/Doc/howto/logging_flow.png index c2d0befe27326c..d60ed7c031585a 100644 Binary files a/Doc/howto/logging_flow.png and b/Doc/howto/logging_flow.png differ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-121035: Update PNG image for logging flow diagram. (GH-121323) (GH-121325)
https://github.com/python/cpython/commit/399e516d28f1604ef52baf52546b4c71e7cd4c84 commit: 399e516d28f1604ef52baf52546b4c71e7cd4c84 branch: 3.12 author: Vinay Sajip committer: vsajip date: 2024-07-03T13:02:02+01:00 summary: [3.12] gh-121035: Update PNG image for logging flow diagram. (GH-121323) (GH-121325) (cherry picked from commit 26d24eeb90d781e381b97d64b4dcb1ee4dd891fe) files: M Doc/howto/logging_flow.png diff --git a/Doc/howto/logging_flow.png b/Doc/howto/logging_flow.png index c2d0befe27326c..d60ed7c031585a 100644 Binary files a/Doc/howto/logging_flow.png and b/Doc/howto/logging_flow.png differ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154)
https://github.com/python/cpython/commit/fb864c76cd5e450e789a7b4095832e118cc49a39 commit: fb864c76cd5e450e789a7b4095832e118cc49a39 branch: main author: Bénédikt Tran <[email protected]> committer: vsajip date: 2024-08-02T12:16:32+01:00 summary: gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154) files: A Misc/NEWS.d/next/Library/2024-07-23-10-59-38.gh-issue-121723.iJEf7e.rst M Doc/library/logging.config.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index dfbf0b1cf2f9ff..0ddbc1a5f88048 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -753,9 +753,12 @@ The ``queue`` and ``listener`` keys are optional. If the ``queue`` key is present, the corresponding value can be one of the following: -* An actual instance of :class:`queue.Queue` or a subclass thereof. This is of course - only possible if you are constructing or modifying the configuration dictionary in - code. +* An object implementing the :class:`queue.Queue` public API. For instance, + this may be an actual instance of :class:`queue.Queue` or a subclass thereof, + or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. + + This is of course only possible if you are constructing or modifying + the configuration dictionary in code. * A string that resolves to a callable which, when called with no arguments, returns the :class:`queue.Queue` instance to use. That callable could be a diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 95e129ae988c24..3781cb1aeb9ae2 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -497,6 +497,33 @@ def as_tuple(self, value): value = tuple(value) return value +def _is_queue_like_object(obj): +"""Check that *obj* implements the Queue API.""" +if isinstance(obj, queue.Queue): +return True +# defer importing multiprocessing as much as possible +from multiprocessing.queues import Queue as MPQueue +if isinstance(obj, MPQueue): +return True +# Depending on the multiprocessing start context, we cannot create +# a multiprocessing.managers.BaseManager instance 'mm' to get the +# runtime type of mm.Queue() or mm.JoinableQueue() (see gh-119819). +# +# Since we only need an object implementing the Queue API, we only +# do a protocol check, but we do not use typing.runtime_checkable() +# and typing.Protocol to reduce import time (see gh-121723). +# +# Ideally, we would have wanted to simply use strict type checking +# instead of a protocol-based type checking since the latter does +# not check the method signatures. +queue_interface = [ +'empty', 'full', 'get', 'get_nowait', +'put', 'put_nowait', 'join', 'qsize', +'task_done', +] +return all(callable(getattr(obj, method, None)) + for method in queue_interface) + class DictConfigurator(BaseConfigurator): """ Configure logging using a dictionary-like object to describe the @@ -791,32 +818,8 @@ def configure_handler(self, config): if '()' not in qspec: raise TypeError('Invalid queue specifier %r' % qspec) config['queue'] = self.configure_custom(dict(qspec)) -else: -from multiprocessing.queues import Queue as MPQueue - -if not isinstance(qspec, (queue.Queue, MPQueue)): -# Safely check if 'qspec' is an instance of Manager.Queue -# / Manager.JoinableQueue - -from multiprocessing import Manager as MM -from multiprocessing.managers import BaseProxy - -# if it's not an instance of BaseProxy, it also can't be -# an instance of Manager.Queue / Manager.JoinableQueue -if isinstance(qspec, BaseProxy): -# Sometimes manager or queue creation might fail -# (e.g. see issue gh-120868). In that case, any -# exception during the creation of these queues will -# propagate up to the caller and be wrapped in a -# `ValueError`, whose cause will indicate the details of -# the failure. -mm = MM() -proxy_queue = mm.Queue() -proxy_joinable_queue = mm.JoinableQueue(
[Python-checkins] [3.12] gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154) (GH-122604)
https://github.com/python/cpython/commit/c47943aa5e937fc052ff9c45e70aefd90a4af0d5 commit: c47943aa5e937fc052ff9c45e70aefd90a4af0d5 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-08-02T12:46:04+01:00 summary: [3.12] gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154) (GH-122604) (cherry picked from commit fb864c76cd5e450e789a7b4095832e118cc49a39) files: A Misc/NEWS.d/next/Library/2024-07-23-10-59-38.gh-issue-121723.iJEf7e.rst M Doc/library/logging.config.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 23aac191f0576c..2722384d174bef 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -752,9 +752,12 @@ The ``queue`` and ``listener`` keys are optional. If the ``queue`` key is present, the corresponding value can be one of the following: -* An actual instance of :class:`queue.Queue` or a subclass thereof. This is of course - only possible if you are constructing or modifying the configuration dictionary in - code. +* An object implementing the :class:`queue.Queue` public API. For instance, + this may be an actual instance of :class:`queue.Queue` or a subclass thereof, + or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. + + This is of course only possible if you are constructing or modifying + the configuration dictionary in code. * A string that resolves to a callable which, when called with no arguments, returns the :class:`queue.Queue` instance to use. That callable could be a diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 1b59876a955a0b..ac90b537d8a396 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -500,6 +500,33 @@ def as_tuple(self, value): value = tuple(value) return value +def _is_queue_like_object(obj): +"""Check that *obj* implements the Queue API.""" +if isinstance(obj, queue.Queue): +return True +# defer importing multiprocessing as much as possible +from multiprocessing.queues import Queue as MPQueue +if isinstance(obj, MPQueue): +return True +# Depending on the multiprocessing start context, we cannot create +# a multiprocessing.managers.BaseManager instance 'mm' to get the +# runtime type of mm.Queue() or mm.JoinableQueue() (see gh-119819). +# +# Since we only need an object implementing the Queue API, we only +# do a protocol check, but we do not use typing.runtime_checkable() +# and typing.Protocol to reduce import time (see gh-121723). +# +# Ideally, we would have wanted to simply use strict type checking +# instead of a protocol-based type checking since the latter does +# not check the method signatures. +queue_interface = [ +'empty', 'full', 'get', 'get_nowait', +'put', 'put_nowait', 'join', 'qsize', +'task_done', +] +return all(callable(getattr(obj, method, None)) + for method in queue_interface) + class DictConfigurator(BaseConfigurator): """ Configure logging using a dictionary-like object to describe the @@ -798,32 +825,8 @@ def configure_handler(self, config): if '()' not in qspec: raise TypeError('Invalid queue specifier %r' % qspec) config['queue'] = self.configure_custom(dict(qspec)) -else: -from multiprocessing.queues import Queue as MPQueue - -if not isinstance(qspec, (queue.Queue, MPQueue)): -# Safely check if 'qspec' is an instance of Manager.Queue -# / Manager.JoinableQueue - -from multiprocessing import Manager as MM -from multiprocessing.managers import BaseProxy - -# if it's not an instance of BaseProxy, it also can't be -# an instance of Manager.Queue / Manager.JoinableQueue -if isinstance(qspec, BaseProxy): -# Sometimes manager or queue creation might fail -# (e.g. see issue gh-120868). In that case, any -# exception during the creation of these queues will -# propagate up to the caller and be wrapped in a -# `ValueError`, whose cause will indicate the details of -# the failure. -mm = MM() -pro
[Python-checkins] [3.13] gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154) (GH-122603)
https://github.com/python/cpython/commit/56435a88c4ffc4ea81b086c2c20b29197e19182e commit: 56435a88c4ffc4ea81b086c2c20b29197e19182e branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-08-02T13:07:27+01:00 summary: [3.13] gh-121723: Relax constraints on queue objects for `logging.handlers.QueueHandler`. (GH-122154) (GH-122603) (cherry picked from commit fb864c76cd5e450e789a7b4095832e118cc49a39) files: A Misc/NEWS.d/next/Library/2024-07-23-10-59-38.gh-issue-121723.iJEf7e.rst M Doc/library/logging.config.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index dfbf0b1cf2f9ff..0ddbc1a5f88048 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -753,9 +753,12 @@ The ``queue`` and ``listener`` keys are optional. If the ``queue`` key is present, the corresponding value can be one of the following: -* An actual instance of :class:`queue.Queue` or a subclass thereof. This is of course - only possible if you are constructing or modifying the configuration dictionary in - code. +* An object implementing the :class:`queue.Queue` public API. For instance, + this may be an actual instance of :class:`queue.Queue` or a subclass thereof, + or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. + + This is of course only possible if you are constructing or modifying + the configuration dictionary in code. * A string that resolves to a callable which, when called with no arguments, returns the :class:`queue.Queue` instance to use. That callable could be a diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 3cc4c57dd8ef80..735bffeaa09884 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -497,6 +497,33 @@ def as_tuple(self, value): value = tuple(value) return value +def _is_queue_like_object(obj): +"""Check that *obj* implements the Queue API.""" +if isinstance(obj, queue.Queue): +return True +# defer importing multiprocessing as much as possible +from multiprocessing.queues import Queue as MPQueue +if isinstance(obj, MPQueue): +return True +# Depending on the multiprocessing start context, we cannot create +# a multiprocessing.managers.BaseManager instance 'mm' to get the +# runtime type of mm.Queue() or mm.JoinableQueue() (see gh-119819). +# +# Since we only need an object implementing the Queue API, we only +# do a protocol check, but we do not use typing.runtime_checkable() +# and typing.Protocol to reduce import time (see gh-121723). +# +# Ideally, we would have wanted to simply use strict type checking +# instead of a protocol-based type checking since the latter does +# not check the method signatures. +queue_interface = [ +'empty', 'full', 'get', 'get_nowait', +'put', 'put_nowait', 'join', 'qsize', +'task_done', +] +return all(callable(getattr(obj, method, None)) + for method in queue_interface) + class DictConfigurator(BaseConfigurator): """ Configure logging using a dictionary-like object to describe the @@ -791,32 +818,8 @@ def configure_handler(self, config): if '()' not in qspec: raise TypeError('Invalid queue specifier %r' % qspec) config['queue'] = self.configure_custom(dict(qspec)) -else: -from multiprocessing.queues import Queue as MPQueue - -if not isinstance(qspec, (queue.Queue, MPQueue)): -# Safely check if 'qspec' is an instance of Manager.Queue -# / Manager.JoinableQueue - -from multiprocessing import Manager as MM -from multiprocessing.managers import BaseProxy - -# if it's not an instance of BaseProxy, it also can't be -# an instance of Manager.Queue / Manager.JoinableQueue -if isinstance(qspec, BaseProxy): -# Sometimes manager or queue creation might fail -# (e.g. see issue gh-120868). In that case, any -# exception during the creation of these queues will -# propagate up to the caller and be wrapped in a -# `ValueError`, whose cause will indicate the details of -# the failure. -mm = MM() -pro
[Python-checkins] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359)
https://github.com/python/cpython/commit/04fabe22dd98b4d87f672254b743fbadd5206352 commit: 04fabe22dd98b4d87f672254b743fbadd5206352 branch: main author: Jérome Perrin committer: vsajip date: 2024-01-16T09:49:24Z summary: gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359) Co-authored-by: Irit Katriel <[email protected]> files: A Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst M Lib/test/test_traceback.py M Lib/traceback.py diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index a6708119b81191..372fc48bf81a6a 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -2209,6 +2209,20 @@ def __repr__(self): err_msg = "b'please do not show me as numbers'" self.assertEqual(self.get_report(e), vanilla + err_msg + '\n') +# an exception with a broken __getattr__ raising a non expected error +class BrokenException(Exception): +broken = False +def __getattr__(self, name): +if self.broken: +raise ValueError(f'no {name}') + +e = BrokenException(123) +vanilla = self.get_report(e) +e.broken = True +self.assertEqual( +self.get_report(e), +vanilla + "Ignored error getting __notes__: ValueError('no __notes__')\n") + def test_exception_with_multiple_notes(self): for e in [ValueError(42), SyntaxError('bad syntax')]: with self.subTest(e=e): diff --git a/Lib/traceback.py b/Lib/traceback.py index 30b42a4f693d95..d27c7a726d2bb6 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1051,7 +1051,11 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line self._str = _safe_string(exc_value, 'exception') -self.__notes__ = getattr(exc_value, '__notes__', None) +try: +self.__notes__ = getattr(exc_value, '__notes__', None) +except Exception as e: +self.__notes__ = [ +f'Ignored error getting __notes__: {_safe_string(e, '__notes__', repr)}'] self._is_syntax_error = False self._have_exc_type = exc_type is not None diff --git a/Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst b/Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst new file mode 100644 index 00..76416553a231a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst @@ -0,0 +1 @@ +Fix rendering tracebacks with exceptions with a broken __getattr__ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-89427: Provide the original prompt value for VIRTUAL_ENV_PROMPT (GH-106726)
https://github.com/python/cpython/commit/8edc8029def8040ebe1caf75d815439156dd2124 commit: 8edc8029def8040ebe1caf75d815439156dd2124 branch: main author: Jim Porter <[email protected]> committer: vsajip date: 2024-01-23T08:53:04Z summary: gh-89427: Provide the original prompt value for VIRTUAL_ENV_PROMPT (GH-106726) This improves the implementation in gh-106643. Previously, venv passed "() " to the activation scripts, but we want to provide the original value so that users can inspect it in the $VIRTUAL_ENV_PROMPT env var. Note: Lib/venv/scripts/common/Activate.ps1 surrounded the prompt value with parens a second time, so no change was necessary in that file. files: M Lib/test/test_venv.py M Lib/venv/__init__.py M Lib/venv/scripts/common/activate M Lib/venv/scripts/nt/activate.bat M Lib/venv/scripts/posix/activate.csh M Lib/venv/scripts/posix/activate.fish diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 6852625c36c62b..6dda00efd7bbb6 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -169,7 +169,7 @@ def test_config_file_command_key(self): ('--clear', 'clear', True), ('--upgrade', 'upgrade', True), ('--upgrade-deps', 'upgrade_deps', True), -('--prompt', 'prompt', True), +('--prompt="foobar"', 'prompt', 'foobar'), ('--without-scm-ignore-files', 'scm_ignore_files', frozenset()), ] for opt, attr, value in options: @@ -201,7 +201,7 @@ def test_prompt(self): self.run_with_capture(builder.create, self.env_dir) context = builder.ensure_directories(self.env_dir) data = self.get_text_file_contents('pyvenv.cfg') -self.assertEqual(context.prompt, '(%s) ' % env_name) +self.assertEqual(context.prompt, env_name) self.assertNotIn("prompt = ", data) rmtree(self.env_dir) @@ -209,7 +209,7 @@ def test_prompt(self): self.run_with_capture(builder.create, self.env_dir) context = builder.ensure_directories(self.env_dir) data = self.get_text_file_contents('pyvenv.cfg') -self.assertEqual(context.prompt, '(My prompt) ') +self.assertEqual(context.prompt, 'My prompt') self.assertIn("prompt = 'My prompt'\n", data) rmtree(self.env_dir) @@ -218,7 +218,7 @@ def test_prompt(self): self.run_with_capture(builder.create, self.env_dir) context = builder.ensure_directories(self.env_dir) data = self.get_text_file_contents('pyvenv.cfg') -self.assertEqual(context.prompt, '(%s) ' % cwd) +self.assertEqual(context.prompt, cwd) self.assertIn("prompt = '%s'\n" % cwd, data) def test_upgrade_dependencies(self): diff --git a/Lib/venv/__init__.py b/Lib/venv/__init__.py index f04ca8fafcc33b..4856594755ae57 100644 --- a/Lib/venv/__init__.py +++ b/Lib/venv/__init__.py @@ -129,8 +129,7 @@ def create_if_needed(d): context = types.SimpleNamespace() context.env_dir = env_dir context.env_name = os.path.split(env_dir)[1] -prompt = self.prompt if self.prompt is not None else context.env_name -context.prompt = '(%s) ' % prompt +context.prompt = self.prompt if self.prompt is not None else context.env_name create_if_needed(env_dir) executable = sys._base_executable if not executable: # see gh-96861 diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index a4e0609045a9d5..cbd4873f012246 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -66,7 +66,7 @@ fi if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then _OLD_VIRTUAL_PS1="${PS1:-}" -PS1="__VENV_PROMPT__${PS1:-}" +PS1="(__VENV_PROMPT__) ${PS1:-}" export PS1 fi diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat index c1c3c82ee37f10..2c98122362a060 100644 --- a/Lib/venv/scripts/nt/activate.bat +++ b/Lib/venv/scripts/nt/activate.bat @@ -16,7 +16,7 @@ if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% set _OLD_VIRTUAL_PROMPT=%PROMPT% -set PROMPT=__VENV_PROMPT__%PROMPT% +set PROMPT=(__VENV_PROMPT__) %PROMPT% if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% set PYTHONHOME= diff --git a/Lib/venv/scripts/posix/activate.csh b/Lib/venv/scripts/posix/activate.csh index 9caf138a919a86..c707f1988b0acc 100644 --- a/Lib/venv/scripts/posix/activate.csh +++ b/Lib/venv/scripts/posix/activate.csh @@ -19,7 +19,7 @@ setenv VIRTUAL_ENV_PROMPT "__VENV_PROMPT__" set
[Python-checkins] gh-114706: Allow QueueListener.stop() to be called more than once. (GH-114748)
https://github.com/python/cpython/commit/e21754d7f8336d4647e28f355d8a3dbd5a2c7545 commit: e21754d7f8336d4647e28f355d8a3dbd5a2c7545 branch: main author: Vinay Sajip committer: vsajip date: 2024-01-30T12:34:18Z summary: gh-114706: Allow QueueListener.stop() to be called more than once. (GH-114748) files: M Lib/logging/handlers.py M Lib/test/test_logging.py diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 9840b7b0aeba88..e7f1322e4ba3d9 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1586,6 +1586,7 @@ def stop(self): 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 +if self._thread: # see gh-114706 - allow calling this more than once +self.enqueue_sentinel() +self._thread.join() +self._thread = None diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 908e242b85f5e7..888523227c2ac4 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4089,6 +4089,7 @@ def test_queue_listener(self): self.que_logger.critical(self.next_message()) finally: listener.stop() +listener.stop() # gh-114706 - ensure no crash if called again self.assertTrue(handler.matches(levelno=logging.WARNING, message='1')) self.assertTrue(handler.matches(levelno=logging.ERROR, message='2')) self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3')) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] Update venv activate.bat to escape custom PROMPT variables on Windows (GH-114885)
https://github.com/python/cpython/commit/7e2703bbff09c3c72c225790e5c71f423351b2d1 commit: 7e2703bbff09c3c72c225790e5c71f423351b2d1 branch: main author: GILGAMESH <[email protected]> committer: vsajip date: 2024-02-02T18:59:53Z summary: Update venv activate.bat to escape custom PROMPT variables on Windows (GH-114885) files: M Lib/venv/scripts/nt/activate.bat diff --git a/Lib/venv/scripts/nt/activate.bat b/Lib/venv/scripts/nt/activate.bat index 2c98122362a060..dd5ea8eb67b90a 100644 --- a/Lib/venv/scripts/nt/activate.bat +++ b/Lib/venv/scripts/nt/activate.bat @@ -15,8 +15,8 @@ if not defined PROMPT set PROMPT=$P$G if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% -set _OLD_VIRTUAL_PROMPT=%PROMPT% -set PROMPT=(__VENV_PROMPT__) %PROMPT% +set "_OLD_VIRTUAL_PROMPT=%PROMPT%" +set "PROMPT=(__VENV_PROMPT__) %PROMPT%" if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% set PYTHONHOME= ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-115032: Update DictConfigurator.configure_formatter() comment about `fmt` retry. (GH-115303)
https://github.com/python/cpython/commit/d823c235495e69fb4c1286b4ed751731bb31bda9
commit: d823c235495e69fb4c1286b4ed751731bb31bda9
branch: main
author: Mariusz Felisiak
committer: vsajip
date: 2024-02-13T08:47:40Z
summary:
gh-115032: Update DictConfigurator.configure_formatter() comment about `fmt`
retry. (GH-115303)
files:
M Lib/logging/config.py
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index de06090942d965..ea37dd7544564a 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -667,10 +667,9 @@ def configure_formatter(self, config):
except TypeError as te:
if "'format'" not in str(te):
raise
-#Name of parameter changed from fmt to format.
-#Retry with old name.
-#This is so that code can be used with older Python versions
-#(e.g. by Django)
+# logging.Formatter and its subclasses expect the `fmt`
+# parameter instead of `format`. Retry passing configuration
+# with `fmt`.
config['fmt'] = config.pop('format')
config['()'] = factory
result = self.configure_custom(config)
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-115811: Update documentation to add some Logger attributes. (GH-116109) (GH-116185)
https://github.com/python/cpython/commit/753b6644e3b342ad0d713cb758f37e6b29a5af04 commit: 753b6644e3b342ad0d713cb758f37e6b29a5af04 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-01T08:57:30Z summary: [3.12] gh-115811: Update documentation to add some Logger attributes. (GH-116109) (GH-116185) (cherry picked from commit 3b6f4cadf19e6a4edd2cbbbc96a0a4024b395648) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 6759544ef21298..fc3af7a2bc5002 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -77,6 +77,27 @@ is the module's name in the Python package namespace. .. class:: Logger + .. attribute:: Logger.name + + This is the logger's name, and is the value that was passed to :func:`getLogger` + to obtain the logger. + + .. note:: This attribute should be treated as read-only. + + .. attribute:: Logger.level + + The threshold of this logger, as set by the :meth:`setLevel` method. + + .. note:: Do not set this attribute directly - always use :meth:`setLevel`, + which has checks for the level passed to it. + + .. attribute:: Logger.parent + + The parent logger of this logger. It may change based on later instantiation + of loggers which are higher up in the namespace hierarchy. + + .. note:: This value should be treated as read-only. + .. attribute:: Logger.propagate If this attribute evaluates to true, events logged to this logger will be @@ -108,6 +129,21 @@ is the module's name in the Python package namespace. scenario is to attach handlers only to the root logger, and to let propagation take care of the rest. + .. attribute:: Logger.handlers + + The list of handlers directly attached to this logger instance. + + .. note:: This attribute should be treated as read-only; it is normally changed via + the :meth:`addHandler` and :meth:`removeHandler` methods, which use locks to ensure + thread-safe operation. + + .. attribute:: Logger.disabled + + This attribute disables handling of any events. It is set to ``False`` in the + initializer, and only changed by logging configuration code. + + .. note:: This attribute should be treated as read-only. + .. method:: Logger.setLevel(level) Sets the threshold for this logger to *level*. Logging messages which are less ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.11] gh-115811: Update documentation to add some Logger attributes. (GH-116109) (GH-116186)
https://github.com/python/cpython/commit/8813e5a2dea3fbea6be7cb4e3db8a64c8665bc90 commit: 8813e5a2dea3fbea6be7cb4e3db8a64c8665bc90 branch: 3.11 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-01T08:58:24Z summary: [3.11] gh-115811: Update documentation to add some Logger attributes. (GH-116109) (GH-116186) (cherry picked from commit 3b6f4cadf19e6a4edd2cbbbc96a0a4024b395648) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 297a4d8d6529bb..ae9a669a721d02 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -77,6 +77,27 @@ is the module's name in the Python package namespace. .. class:: Logger + .. attribute:: Logger.name + + This is the logger's name, and is the value that was passed to :func:`getLogger` + to obtain the logger. + + .. note:: This attribute should be treated as read-only. + + .. attribute:: Logger.level + + The threshold of this logger, as set by the :meth:`setLevel` method. + + .. note:: Do not set this attribute directly - always use :meth:`setLevel`, + which has checks for the level passed to it. + + .. attribute:: Logger.parent + + The parent logger of this logger. It may change based on later instantiation + of loggers which are higher up in the namespace hierarchy. + + .. note:: This value should be treated as read-only. + .. attribute:: Logger.propagate If this attribute evaluates to true, events logged to this logger will be @@ -108,6 +129,21 @@ is the module's name in the Python package namespace. scenario is to attach handlers only to the root logger, and to let propagation take care of the rest. + .. attribute:: Logger.handlers + + The list of handlers directly attached to this logger instance. + + .. note:: This attribute should be treated as read-only; it is normally changed via + the :meth:`addHandler` and :meth:`removeHandler` methods, which use locks to ensure + thread-safe operation. + + .. attribute:: Logger.disabled + + This attribute disables handling of any events. It is set to ``False`` in the + initializer, and only changed by logging configuration code. + + .. note:: This attribute should be treated as read-only. + .. method:: Logger.setLevel(level) Sets the threshold for this logger to *level*. Logging messages which are less ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [doc]: Update cookbook recipe for Qt6. (GH-116719)
https://github.com/python/cpython/commit/186af3cf21705badec086ec16f231ac390747d3b
commit: 186af3cf21705badec086ec16f231ac390747d3b
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-03-13T13:22:47Z
summary:
[doc]: Update cookbook recipe for Qt6. (GH-116719)
files:
M Doc/howto/logging-cookbook.rst
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index f7d885ec88483d..ad3e34d0b33bd2 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -3418,9 +3418,10 @@ The worker thread is implemented using Qt's ``QThread``
class rather than the
:mod:`threading` module, as there are circumstances where one has to use
``QThread``, which offers better integration with other ``Qt`` components.
-The code should work with recent releases of either ``PySide2`` or ``PyQt5``.
-You should be able to adapt the approach to earlier versions of Qt. Please
-refer to the comments in the code snippet for more detailed information.
+The code should work with recent releases of either ``PySide6``, ``PyQt6``,
+``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier
+versions of Qt. Please refer to the comments in the code snippet for more
+detailed information.
.. code-block:: python3
@@ -3430,16 +3431,25 @@ refer to the comments in the code snippet for more
detailed information.
import sys
import time
-# Deal with minor differences between PySide2 and PyQt5
+# Deal with minor differences between different Qt packages
try:
-from PySide2 import QtCore, QtGui, QtWidgets
+from PySide6 import QtCore, QtGui, QtWidgets
Signal = QtCore.Signal
Slot = QtCore.Slot
except ImportError:
-from PyQt5 import QtCore, QtGui, QtWidgets
-Signal = QtCore.pyqtSignal
-Slot = QtCore.pyqtSlot
-
+try:
+from PyQt6 import QtCore, QtGui, QtWidgets
+Signal = QtCore.pyqtSignal
+Slot = QtCore.pyqtSlot
+except ImportError:
+try:
+from PySide2 import QtCore, QtGui, QtWidgets
+Signal = QtCore.Signal
+Slot = QtCore.Slot
+except ImportError:
+from PyQt5 import QtCore, QtGui, QtWidgets
+Signal = QtCore.pyqtSignal
+Slot = QtCore.pyqtSlot
logger = logging.getLogger(__name__)
@@ -3511,8 +3521,14 @@ refer to the comments in the code snippet for more
detailed information.
while not QtCore.QThread.currentThread().isInterruptionRequested():
delay = 0.5 + random.random() * 2
time.sleep(delay)
-level = random.choice(LEVELS)
-logger.log(level, 'Message after delay of %3.1f: %d', delay,
i, extra=extra)
+try:
+if random.random() < 0.1:
+raise ValueError('Exception raised: %d' % i)
+else:
+level = random.choice(LEVELS)
+logger.log(level, 'Message after delay of %3.1f: %d',
delay, i, extra=extra)
+except ValueError as e:
+logger.exception('Failed: %s', e, extra=extra)
i += 1
#
@@ -3539,7 +3555,10 @@ refer to the comments in the code snippet for more
detailed information.
self.textedit = te = QtWidgets.QPlainTextEdit(self)
# Set whatever the default monospace font is for the platform
f = QtGui.QFont('nosuchfont')
-f.setStyleHint(f.Monospace)
+if hasattr(f, 'Monospace'):
+f.setStyleHint(f.Monospace)
+else:
+f.setStyleHint(f.StyleHint.Monospace) # for Qt6
te.setFont(f)
te.setReadOnly(True)
PB = QtWidgets.QPushButton
@@ -3626,7 +3645,11 @@ refer to the comments in the code snippet for more
detailed information.
app = QtWidgets.QApplication(sys.argv)
example = Window(app)
example.show()
-sys.exit(app.exec_())
+if hasattr(app, 'exec'):
+rc = app.exec()
+else:
+rc = app.exec_()
+sys.exit(rc)
if __name__=='__main__':
main()
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.11] [doc]: Update cookbook recipe for Qt6. (GH-116719) (GH-116728)
https://github.com/python/cpython/commit/1117e7dd1b5264e20ff5f4474fd3f5c72e675aac commit: 1117e7dd1b5264e20ff5f4474fd3f5c72e675aac branch: 3.11 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-13T14:34:09Z summary: [3.11] [doc]: Update cookbook recipe for Qt6. (GH-116719) (GH-116728) (cherry picked from commit 186af3cf21705badec086ec16f231ac390747d3b) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 96fcafc7e64942..e0b16aad9c1fc0 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -3418,9 +3418,10 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the :mod:`threading` module, as there are circumstances where one has to use ``QThread``, which offers better integration with other ``Qt`` components. -The code should work with recent releases of either ``PySide2`` or ``PyQt5``. -You should be able to adapt the approach to earlier versions of Qt. Please -refer to the comments in the code snippet for more detailed information. +The code should work with recent releases of either ``PySide6``, ``PyQt6``, +``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier +versions of Qt. Please refer to the comments in the code snippet for more +detailed information. .. code-block:: python3 @@ -3430,16 +3431,25 @@ refer to the comments in the code snippet for more detailed information. import sys import time -# Deal with minor differences between PySide2 and PyQt5 +# Deal with minor differences between different Qt packages try: -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets Signal = QtCore.Signal Slot = QtCore.Slot except ImportError: -from PyQt5 import QtCore, QtGui, QtWidgets -Signal = QtCore.pyqtSignal -Slot = QtCore.pyqtSlot - +try: +from PyQt6 import QtCore, QtGui, QtWidgets +Signal = QtCore.pyqtSignal +Slot = QtCore.pyqtSlot +except ImportError: +try: +from PySide2 import QtCore, QtGui, QtWidgets +Signal = QtCore.Signal +Slot = QtCore.Slot +except ImportError: +from PyQt5 import QtCore, QtGui, QtWidgets +Signal = QtCore.pyqtSignal +Slot = QtCore.pyqtSlot logger = logging.getLogger(__name__) @@ -3511,8 +3521,14 @@ refer to the comments in the code snippet for more detailed information. while not QtCore.QThread.currentThread().isInterruptionRequested(): delay = 0.5 + random.random() * 2 time.sleep(delay) -level = random.choice(LEVELS) -logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra) +try: +if random.random() < 0.1: +raise ValueError('Exception raised: %d' % i) +else: +level = random.choice(LEVELS) +logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra) +except ValueError as e: +logger.exception('Failed: %s', e, extra=extra) i += 1 # @@ -3539,7 +3555,10 @@ refer to the comments in the code snippet for more detailed information. self.textedit = te = QtWidgets.QPlainTextEdit(self) # Set whatever the default monospace font is for the platform f = QtGui.QFont('nosuchfont') -f.setStyleHint(f.Monospace) +if hasattr(f, 'Monospace'): +f.setStyleHint(f.Monospace) +else: +f.setStyleHint(f.StyleHint.Monospace) # for Qt6 te.setFont(f) te.setReadOnly(True) PB = QtWidgets.QPushButton @@ -3626,7 +3645,11 @@ refer to the comments in the code snippet for more detailed information. app = QtWidgets.QApplication(sys.argv) example = Window(app) example.show() -sys.exit(app.exec_()) +if hasattr(app, 'exec'): +rc = app.exec() +else: +rc = app.exec_() +sys.exit(rc) if __name__=='__main__': main() ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] [doc]: Update cookbook recipe for Qt6. (GH-116719) (GH-116729)
https://github.com/python/cpython/commit/8e45f7d2aecd8820fc2f144f0bb0159351e46ab1 commit: 8e45f7d2aecd8820fc2f144f0bb0159351e46ab1 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-13T14:33:31Z summary: [3.12] [doc]: Update cookbook recipe for Qt6. (GH-116719) (GH-116729) (cherry picked from commit 186af3cf21705badec086ec16f231ac390747d3b) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 466e1e21cbd187..189bd38d4e5d1d 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -3418,9 +3418,10 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the :mod:`threading` module, as there are circumstances where one has to use ``QThread``, which offers better integration with other ``Qt`` components. -The code should work with recent releases of either ``PySide2`` or ``PyQt5``. -You should be able to adapt the approach to earlier versions of Qt. Please -refer to the comments in the code snippet for more detailed information. +The code should work with recent releases of either ``PySide6``, ``PyQt6``, +``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier +versions of Qt. Please refer to the comments in the code snippet for more +detailed information. .. code-block:: python3 @@ -3430,16 +3431,25 @@ refer to the comments in the code snippet for more detailed information. import sys import time -# Deal with minor differences between PySide2 and PyQt5 +# Deal with minor differences between different Qt packages try: -from PySide2 import QtCore, QtGui, QtWidgets +from PySide6 import QtCore, QtGui, QtWidgets Signal = QtCore.Signal Slot = QtCore.Slot except ImportError: -from PyQt5 import QtCore, QtGui, QtWidgets -Signal = QtCore.pyqtSignal -Slot = QtCore.pyqtSlot - +try: +from PyQt6 import QtCore, QtGui, QtWidgets +Signal = QtCore.pyqtSignal +Slot = QtCore.pyqtSlot +except ImportError: +try: +from PySide2 import QtCore, QtGui, QtWidgets +Signal = QtCore.Signal +Slot = QtCore.Slot +except ImportError: +from PyQt5 import QtCore, QtGui, QtWidgets +Signal = QtCore.pyqtSignal +Slot = QtCore.pyqtSlot logger = logging.getLogger(__name__) @@ -3511,8 +3521,14 @@ refer to the comments in the code snippet for more detailed information. while not QtCore.QThread.currentThread().isInterruptionRequested(): delay = 0.5 + random.random() * 2 time.sleep(delay) -level = random.choice(LEVELS) -logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra) +try: +if random.random() < 0.1: +raise ValueError('Exception raised: %d' % i) +else: +level = random.choice(LEVELS) +logger.log(level, 'Message after delay of %3.1f: %d', delay, i, extra=extra) +except ValueError as e: +logger.exception('Failed: %s', e, extra=extra) i += 1 # @@ -3539,7 +3555,10 @@ refer to the comments in the code snippet for more detailed information. self.textedit = te = QtWidgets.QPlainTextEdit(self) # Set whatever the default monospace font is for the platform f = QtGui.QFont('nosuchfont') -f.setStyleHint(f.Monospace) +if hasattr(f, 'Monospace'): +f.setStyleHint(f.Monospace) +else: +f.setStyleHint(f.StyleHint.Monospace) # for Qt6 te.setFont(f) te.setReadOnly(True) PB = QtWidgets.QPushButton @@ -3626,7 +3645,11 @@ refer to the comments in the code snippet for more detailed information. app = QtWidgets.QApplication(sys.argv) example = Window(app) example.show() -sys.exit(app.exec_()) +if hasattr(app, 'exec'): +rc = app.exec() +else: +rc = app.exec_() +sys.exit(rc) if __name__=='__main__': main() ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-98731: Improvements to the logging documentation (GH-101618)
https://github.com/python/cpython/commit/7f418fb111dec325b5c9fe6f6e96076049322f02 commit: 7f418fb111dec325b5c9fe6f6e96076049322f02 branch: main author: Nir Friedman committer: vsajip date: 2024-03-13T15:58:30Z summary: gh-98731: Improvements to the logging documentation (GH-101618) Co-authored-by: Vinay Sajip files: M Doc/howto/logging.rst M Doc/library/logging.rst diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 347330e98dd00c..ab758a885b3556 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -25,10 +25,12 @@ or *severity*. When to use logging ^^^ -Logging provides a set of convenience functions for simple logging usage. These -are :func:`debug`, :func:`info`, :func:`warning`, :func:`error` and -:func:`critical`. To determine when to use logging, see the table below, which -states, for each of a set of common tasks, the best tool to use for it. +You can access logging functionality by creating a logger via ``logger = +getLogger(__name__)``, and then calling the logger's :meth:`~Logger.debug`, +:meth:`~Logger.info`, :meth:`~Logger.warning`, :meth:`~Logger.error` and +:meth:`~Logger.critical` methods. To determine when to use logging, and to see +which logger methods to use when, see the table below. It states, for each of a +set of common tasks, the best tool to use for that task. +-+--+ | Task you want to perform| The best tool for the task | @@ -37,8 +39,8 @@ states, for each of a set of common tasks, the best tool to use for it. | usage of a command line script or | | | program | | +-+--+ -| Report events that occur during | :func:`logging.info` (or | -| normal operation of a program (e.g. | :func:`logging.debug` for very | +| Report events that occur during | A logger's :meth:`~Logger.info` (or | +| normal operation of a program (e.g. | :meth:`~Logger.debug` method for very| | for status monitoring or fault | detailed output for diagnostic | | investigation) | purposes)| +-+--+ @@ -47,22 +49,23 @@ states, for each of a set of common tasks, the best tool to use for it. | | the client application should be | | | modified to eliminate the warning| | | | -| | :func:`logging.warning` if there is | -| | nothing the client application can do| -| | about the situation, but the event | -| | should still be noted| +| | A logger's :meth:`~Logger.warning` | +| | method if there is nothing the client| +| | application can do about the | +| | situation, but the event should still| +| | be noted | +-+--+ | Report an error regarding a | Raise an exception | | particular runtime event| | +-+--+ -| Report suppression of an error | :func:`logging.error`, | -| without raising an exception (e.g. | :func:`logging.exception` or | -| error handler in a long-running | :func:`logging.critical` as | +| Report suppression of an error | A logger's :meth:`~Logger.error`,| +| without raising an exception (e.g. | :meth:`~Logger.exception` or | +| error handler in a long-running | :meth:`~Logger.critical` method as | | server process) | appropriate for the specific error | | | and application domain | +-+--+ -The logging functions are named after the level or severity of the events +The logger methods are named after the level or severity of the events they are used to track. The standard levels and their applicability are described below (in increasing order of severity): @@ -115,12 +118,18 @@ If you type these lines into a script and run it, you'll see: WARNING:root:Watch out! printed out on the console. The ``INFO`` message doesn't appear
[Python-checkins] [3.11] gh-98731: Improvements to the logging documentation (GH-101618) (GH-116733)
https://github.com/python/cpython/commit/039fd9ec344beafa02d3143a517781612b516f5b commit: 039fd9ec344beafa02d3143a517781612b516f5b branch: 3.11 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-13T17:16:28Z summary: [3.11] gh-98731: Improvements to the logging documentation (GH-101618) (GH-116733) (cherry picked from commit 7f418fb111dec325b5c9fe6f6e96076049322f02) files: M Doc/howto/logging.rst M Doc/library/logging.rst diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 9bdb4a84e29ec2..ed5e71cec90112 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -22,10 +22,12 @@ or *severity*. When to use logging ^^^ -Logging provides a set of convenience functions for simple logging usage. These -are :func:`debug`, :func:`info`, :func:`warning`, :func:`error` and -:func:`critical`. To determine when to use logging, see the table below, which -states, for each of a set of common tasks, the best tool to use for it. +You can access logging functionality by creating a logger via ``logger = +getLogger(__name__)``, and then calling the logger's :meth:`~Logger.debug`, +:meth:`~Logger.info`, :meth:`~Logger.warning`, :meth:`~Logger.error` and +:meth:`~Logger.critical` methods. To determine when to use logging, and to see +which logger methods to use when, see the table below. It states, for each of a +set of common tasks, the best tool to use for that task. +-+--+ | Task you want to perform| The best tool for the task | @@ -34,8 +36,8 @@ states, for each of a set of common tasks, the best tool to use for it. | usage of a command line script or | | | program | | +-+--+ -| Report events that occur during | :func:`logging.info` (or | -| normal operation of a program (e.g. | :func:`logging.debug` for very | +| Report events that occur during | A logger's :meth:`~Logger.info` (or | +| normal operation of a program (e.g. | :meth:`~Logger.debug` method for very| | for status monitoring or fault | detailed output for diagnostic | | investigation) | purposes)| +-+--+ @@ -44,22 +46,23 @@ states, for each of a set of common tasks, the best tool to use for it. | | the client application should be | | | modified to eliminate the warning| | | | -| | :func:`logging.warning` if there is | -| | nothing the client application can do| -| | about the situation, but the event | -| | should still be noted| +| | A logger's :meth:`~Logger.warning` | +| | method if there is nothing the client| +| | application can do about the | +| | situation, but the event should still| +| | be noted | +-+--+ | Report an error regarding a | Raise an exception | | particular runtime event| | +-+--+ -| Report suppression of an error | :func:`logging.error`, | -| without raising an exception (e.g. | :func:`logging.exception` or | -| error handler in a long-running | :func:`logging.critical` as | +| Report suppression of an error | A logger's :meth:`~Logger.error`,| +| without raising an exception (e.g. | :meth:`~Logger.exception` or | +| error handler in a long-running | :meth:`~Logger.critical` method as | | server process) | appropriate for the specific error | | | and application domain | +-+--+ -The logging functions are named after the level or severity of the events +The logger methods are named after the level or severity of the events they are used to track. The standard levels and their applicability are described below (in increasing order of severity): @@ -113,12 +116,18 @@ If you type these lines into a scri
[Python-checkins] [3.12] gh-98731: Improvements to the logging documentation (GH-101618) (GH-116734)
https://github.com/python/cpython/commit/0cca76243ce2513a70f45a0612286c7ee4fc3450 commit: 0cca76243ce2513a70f45a0612286c7ee4fc3450 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-13T17:16:48Z summary: [3.12] gh-98731: Improvements to the logging documentation (GH-101618) (GH-116734) (cherry picked from commit 7f418fb111dec325b5c9fe6f6e96076049322f02) files: M Doc/howto/logging.rst M Doc/library/logging.rst diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst index 55c7c02e98ddb6..877cb24328c160 100644 --- a/Doc/howto/logging.rst +++ b/Doc/howto/logging.rst @@ -25,10 +25,12 @@ or *severity*. When to use logging ^^^ -Logging provides a set of convenience functions for simple logging usage. These -are :func:`debug`, :func:`info`, :func:`warning`, :func:`error` and -:func:`critical`. To determine when to use logging, see the table below, which -states, for each of a set of common tasks, the best tool to use for it. +You can access logging functionality by creating a logger via ``logger = +getLogger(__name__)``, and then calling the logger's :meth:`~Logger.debug`, +:meth:`~Logger.info`, :meth:`~Logger.warning`, :meth:`~Logger.error` and +:meth:`~Logger.critical` methods. To determine when to use logging, and to see +which logger methods to use when, see the table below. It states, for each of a +set of common tasks, the best tool to use for that task. +-+--+ | Task you want to perform| The best tool for the task | @@ -37,8 +39,8 @@ states, for each of a set of common tasks, the best tool to use for it. | usage of a command line script or | | | program | | +-+--+ -| Report events that occur during | :func:`logging.info` (or | -| normal operation of a program (e.g. | :func:`logging.debug` for very | +| Report events that occur during | A logger's :meth:`~Logger.info` (or | +| normal operation of a program (e.g. | :meth:`~Logger.debug` method for very| | for status monitoring or fault | detailed output for diagnostic | | investigation) | purposes)| +-+--+ @@ -47,22 +49,23 @@ states, for each of a set of common tasks, the best tool to use for it. | | the client application should be | | | modified to eliminate the warning| | | | -| | :func:`logging.warning` if there is | -| | nothing the client application can do| -| | about the situation, but the event | -| | should still be noted| +| | A logger's :meth:`~Logger.warning` | +| | method if there is nothing the client| +| | application can do about the | +| | situation, but the event should still| +| | be noted | +-+--+ | Report an error regarding a | Raise an exception | | particular runtime event| | +-+--+ -| Report suppression of an error | :func:`logging.error`, | -| without raising an exception (e.g. | :func:`logging.exception` or | -| error handler in a long-running | :func:`logging.critical` as | +| Report suppression of an error | A logger's :meth:`~Logger.error`,| +| without raising an exception (e.g. | :meth:`~Logger.exception` or | +| error handler in a long-running | :meth:`~Logger.critical` method as | | server process) | appropriate for the specific error | | | and application domain | +-+--+ -The logging functions are named after the level or severity of the events +The logger methods are named after the level or severity of the events they are used to track. The standard levels and their applicability are described below (in increasing order of severity): @@ -116,12 +119,18 @@ If you type these lines into a scri
[Python-checkins] [docs] Fix typo in docstring and add example to logging cookbook. (GH-117157)
https://github.com/python/cpython/commit/00baaa21de229a6db80ff2b84c2fd6ad1999a24c
commit: 00baaa21de229a6db80ff2b84c2fd6ad1999a24c
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-03-22T17:25:51Z
summary:
[docs] Fix typo in docstring and add example to logging cookbook. (GH-117157)
files:
M Doc/howto/logging-cookbook.rst
M Lib/logging/__init__.py
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index ad3e34d0b33bd2..d8ebeabcd522b1 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -1846,8 +1846,11 @@ the use of a :class:`Filter` does not provide the
desired result.
.. _zeromq-handlers:
-Subclassing QueueHandler - a ZeroMQ example
+Subclassing QueueHandler and QueueListener- a ZeroMQ example
+
+
+Subclass ``QueueHandler``
+^
You can use a :class:`QueueHandler` subclass to send messages to other kinds
of queues, for example a ZeroMQ 'publish' socket. In the example below,the
@@ -1885,8 +1888,8 @@ data needed by the handler to create the socket::
self.queue.close()
-Subclassing QueueListener - a ZeroMQ example
-
+Subclass ``QueueListener``
+^^
You can also subclass :class:`QueueListener` to get messages from other kinds
of queues, for example a ZeroMQ 'subscribe' socket. Here's an example::
@@ -1903,25 +1906,134 @@ of queues, for example a ZeroMQ 'subscribe' socket.
Here's an example::
msg = self.queue.recv_json()
return logging.makeLogRecord(msg)
+.. _pynng-handlers:
-.. seealso::
+Subclassing QueueHandler and QueueListener- a ``pynng`` example
+---
- Module :mod:`logging`
- API reference for the logging module.
+In a similar way to the above section, we can implement a listener and handler
+using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to
+`NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ.
+The following snippets illustrate -- you can test them in an environment which
has
+``pynng`` installed. Juat for variety, we present the listener first.
- Module :mod:`logging.config`
- Configuration API for the logging module.
- Module :mod:`logging.handlers`
- Useful handlers included with the logging module.
+Subclass ``QueueListener``
+^^
+
+.. code-block:: python
+
+import json
+import logging
+import logging.handlers
+
+import pynng
- :ref:`A basic logging tutorial `
+DEFAULT_ADDR = "tcp://localhost:13232"
- :ref:`A more advanced logging tutorial `
+interrupted = False
+class NNGSocketListener(logging.handlers.QueueListener):
+
+def __init__(self, uri, /, *handlers, **kwargs):
+# Have a timeout for interruptability, and open a
+# subscriber socket
+socket = pynng.Sub0(listen=uri, recv_timeout=500)
+# The b'' subscription matches all topics
+topics = kwargs.pop('topics', None) or b''
+socket.subscribe(topics)
+# We treat the socket as a queue
+super().__init__(socket, *handlers, **kwargs)
+
+def dequeue(self, block):
+data = None
+# Keep looping while not interrupted and no data received over the
+# socket
+while not interrupted:
+try:
+data = self.queue.recv(block=block)
+break
+except pynng.Timeout:
+pass
+except pynng.Closed: # sometimes hit when you hit Ctrl-C
+break
+if data is None:
+return None
+# Get the logging event sent from a publisher
+event = json.loads(data.decode('utf-8'))
+return logging.makeLogRecord(event)
+
+def enqueue_sentinel(self):
+# Not used in this implementation, as the socket isn't really a
+# queue
+pass
+
+logging.getLogger('pynng').propagate = False
+listener = NNGSocketListener(DEFAULT_ADDR, logging.StreamHandler(),
topics=b'')
+listener.start()
+print('Press Ctrl-C to stop.')
+try:
+while True:
+pass
+except KeyboardInterrupt:
+interrupted = True
+finally:
+listener.stop()
+
+
+Subclass ``QueueHandler``
+^
.. currentmodule:: logging
+.. code-block:: python
+
+import json
+import logging
+import logging.handlers
+import time
+import random
+
+import pynng
+
+DEFAULT_ADDR = "tcp://localhost:1323
[Python-checkins] [3.12] [docs] Fix typo in docstring and add example to logging cookbook. (GH-117157) (GH-117159)
https://github.com/python/cpython/commit/135a698d5cbb9090fa980efcbb15bcf6063ad5b7 commit: 135a698d5cbb9090fa980efcbb15bcf6063ad5b7 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-22T17:50:02Z summary: [3.12] [docs] Fix typo in docstring and add example to logging cookbook. (GH-117157) (GH-117159) (cherry picked from commit 00baaa21de229a6db80ff2b84c2fd6ad1999a24c) files: M Doc/howto/logging-cookbook.rst M Lib/logging/__init__.py diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 189bd38d4e5d1d..fe13a7cbe8bcf9 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1846,8 +1846,11 @@ the use of a :class:`Filter` does not provide the desired result. .. _zeromq-handlers: -Subclassing QueueHandler - a ZeroMQ example +Subclassing QueueHandler and QueueListener- a ZeroMQ example + + +Subclass ``QueueHandler`` +^ You can use a :class:`QueueHandler` subclass to send messages to other kinds of queues, for example a ZeroMQ 'publish' socket. In the example below,the @@ -1885,8 +1888,8 @@ data needed by the handler to create the socket:: self.queue.close() -Subclassing QueueListener - a ZeroMQ example - +Subclass ``QueueListener`` +^^ You can also subclass :class:`QueueListener` to get messages from other kinds of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: @@ -1903,25 +1906,134 @@ of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: msg = self.queue.recv_json() return logging.makeLogRecord(msg) +.. _pynng-handlers: -.. seealso:: +Subclassing QueueHandler and QueueListener- a ``pynng`` example +--- - Module :mod:`logging` - API reference for the logging module. +In a similar way to the above section, we can implement a listener and handler +using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to +`NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ. +The following snippets illustrate -- you can test them in an environment which has +``pynng`` installed. Juat for variety, we present the listener first. - Module :mod:`logging.config` - Configuration API for the logging module. - Module :mod:`logging.handlers` - Useful handlers included with the logging module. +Subclass ``QueueListener`` +^^ + +.. code-block:: python + +import json +import logging +import logging.handlers + +import pynng - :ref:`A basic logging tutorial ` +DEFAULT_ADDR = "tcp://localhost:13232" - :ref:`A more advanced logging tutorial ` +interrupted = False +class NNGSocketListener(logging.handlers.QueueListener): + +def __init__(self, uri, /, *handlers, **kwargs): +# Have a timeout for interruptability, and open a +# subscriber socket +socket = pynng.Sub0(listen=uri, recv_timeout=500) +# The b'' subscription matches all topics +topics = kwargs.pop('topics', None) or b'' +socket.subscribe(topics) +# We treat the socket as a queue +super().__init__(socket, *handlers, **kwargs) + +def dequeue(self, block): +data = None +# Keep looping while not interrupted and no data received over the +# socket +while not interrupted: +try: +data = self.queue.recv(block=block) +break +except pynng.Timeout: +pass +except pynng.Closed: # sometimes hit when you hit Ctrl-C +break +if data is None: +return None +# Get the logging event sent from a publisher +event = json.loads(data.decode('utf-8')) +return logging.makeLogRecord(event) + +def enqueue_sentinel(self): +# Not used in this implementation, as the socket isn't really a +# queue +pass + +logging.getLogger('pynng').propagate = False +listener = NNGSocketListener(DEFAULT_ADDR, logging.StreamHandler(), topics=b'') +listener.start() +print('Press Ctrl-C to stop.') +try: +while True: +pass +except KeyboardInterrupt: +interrupted = True +finally: +listener.stop() + + +Subclass ``QueueHandler`` +^ .. currentmodule:: logging +.. code-block:: python + +import json
[Python-checkins] [3.11] [docs] Fix typo in docstring and add example to logging cookbook. (GH-117157) (GH-117158)
https://github.com/python/cpython/commit/de43ce11ccee0c584027edc70fd1c9e9e21ed66b commit: de43ce11ccee0c584027edc70fd1c9e9e21ed66b branch: 3.11 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-03-23T10:19:14Z summary: [3.11] [docs] Fix typo in docstring and add example to logging cookbook. (GH-117157) (GH-117158) (cherry picked from commit 00baaa21de229a6db80ff2b84c2fd6ad1999a24c) files: M Doc/howto/logging-cookbook.rst M Lib/logging/__init__.py diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index e0b16aad9c1fc0..73f63f4865ec69 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1846,8 +1846,11 @@ the use of a :class:`Filter` does not provide the desired result. .. _zeromq-handlers: -Subclassing QueueHandler - a ZeroMQ example +Subclassing QueueHandler and QueueListener- a ZeroMQ example + + +Subclass ``QueueHandler`` +^ You can use a :class:`QueueHandler` subclass to send messages to other kinds of queues, for example a ZeroMQ 'publish' socket. In the example below,the @@ -1885,8 +1888,8 @@ data needed by the handler to create the socket:: self.queue.close() -Subclassing QueueListener - a ZeroMQ example - +Subclass ``QueueListener`` +^^ You can also subclass :class:`QueueListener` to get messages from other kinds of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: @@ -1903,25 +1906,134 @@ of queues, for example a ZeroMQ 'subscribe' socket. Here's an example:: msg = self.queue.recv_json() return logging.makeLogRecord(msg) +.. _pynng-handlers: -.. seealso:: +Subclassing QueueHandler and QueueListener- a ``pynng`` example +--- - Module :mod:`logging` - API reference for the logging module. +In a similar way to the above section, we can implement a listener and handler +using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to +`NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ. +The following snippets illustrate -- you can test them in an environment which has +``pynng`` installed. Juat for variety, we present the listener first. - Module :mod:`logging.config` - Configuration API for the logging module. - Module :mod:`logging.handlers` - Useful handlers included with the logging module. +Subclass ``QueueListener`` +^^ + +.. code-block:: python + +import json +import logging +import logging.handlers + +import pynng - :ref:`A basic logging tutorial ` +DEFAULT_ADDR = "tcp://localhost:13232" - :ref:`A more advanced logging tutorial ` +interrupted = False +class NNGSocketListener(logging.handlers.QueueListener): + +def __init__(self, uri, /, *handlers, **kwargs): +# Have a timeout for interruptability, and open a +# subscriber socket +socket = pynng.Sub0(listen=uri, recv_timeout=500) +# The b'' subscription matches all topics +topics = kwargs.pop('topics', None) or b'' +socket.subscribe(topics) +# We treat the socket as a queue +super().__init__(socket, *handlers, **kwargs) + +def dequeue(self, block): +data = None +# Keep looping while not interrupted and no data received over the +# socket +while not interrupted: +try: +data = self.queue.recv(block=block) +break +except pynng.Timeout: +pass +except pynng.Closed: # sometimes hit when you hit Ctrl-C +break +if data is None: +return None +# Get the logging event sent from a publisher +event = json.loads(data.decode('utf-8')) +return logging.makeLogRecord(event) + +def enqueue_sentinel(self): +# Not used in this implementation, as the socket isn't really a +# queue +pass + +logging.getLogger('pynng').propagate = False +listener = NNGSocketListener(DEFAULT_ADDR, logging.StreamHandler(), topics=b'') +listener.start() +print('Press Ctrl-C to stop.') +try: +while True: +pass +except KeyboardInterrupt: +interrupted = True +finally: +listener.stop() + + +Subclass ``QueueHandler`` +^ .. currentmodule:: logging +.. code-block:: python + +import json
[Python-checkins] gh-112571: Move fish venv activation script into the common folder (GH-117169)
https://github.com/python/cpython/commit/83485a095363dad6c97b19af2826ca0c34343bfc commit: 83485a095363dad6c97b19af2826ca0c34343bfc branch: main author: Totally a booplicate <[email protected]> committer: vsajip date: 2024-03-24T15:48:40Z summary: gh-112571: Move fish venv activation script into the common folder (GH-117169) pythongh-112571: allow using fish venv activation script on windows The fish shell can be used on windows under cygwin or msys2. This change moves the script to the common folder so the venv module will install it on both posix and nt systems (like the bash script). files: A Lib/venv/scripts/common/activate.fish D Lib/venv/scripts/posix/activate.fish diff --git a/Lib/venv/scripts/posix/activate.fish b/Lib/venv/scripts/common/activate.fish similarity index 100% rename from Lib/venv/scripts/posix/activate.fish rename to Lib/venv/scripts/common/activate.fish ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [doc] Update logging documentation to improve grammar and elucidate an example. (GH-117541)
https://github.com/python/cpython/commit/df912c913a3d94995b379f1e19fe0a79acab6169
commit: df912c913a3d94995b379f1e19fe0a79acab6169
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-04-04T13:14:44+01:00
summary:
[doc] Update logging documentation to improve grammar and elucidate an example.
(GH-117541)
files:
M Doc/howto/logging-cookbook.rst
M Doc/library/logging.rst
diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst
index d8ebeabcd522b1..61723bc6cf256a 100644
--- a/Doc/howto/logging-cookbook.rst
+++ b/Doc/howto/logging-cookbook.rst
@@ -1915,7 +1915,7 @@ In a similar way to the above section, we can implement a
listener and handler
using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to
`NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ.
The following snippets illustrate -- you can test them in an environment which
has
-``pynng`` installed. Juat for variety, we present the listener first.
+``pynng`` installed. Just for variety, we present the listener first.
Subclass ``QueueListener``
@@ -1923,6 +1923,7 @@ Subclass ``QueueListener``
.. code-block:: python
+# listener.py
import json
import logging
import logging.handlers
@@ -1955,7 +1956,7 @@ Subclass ``QueueListener``
break
except pynng.Timeout:
pass
-except pynng.Closed: # sometimes hit when you hit Ctrl-C
+except pynng.Closed: # sometimes happens when you hit Ctrl-C
break
if data is None:
return None
@@ -1988,6 +1989,7 @@ Subclass ``QueueHandler``
.. code-block:: python
+# sender.py
import json
import logging
import logging.handlers
@@ -2015,9 +2017,10 @@ Subclass ``QueueHandler``
logging.getLogger('pynng').propagate = False
handler = NNGSocketHandler(DEFAULT_ADDR)
+# Make sure the process ID is in the output
logging.basicConfig(level=logging.DEBUG,
handlers=[logging.StreamHandler(), handler],
-format='%(levelname)-8s %(name)10s %(message)s')
+format='%(levelname)-8s %(name)10s %(process)6s
%(message)s')
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
logging.CRITICAL)
logger_names = ('myapp', 'myapp.lib1', 'myapp.lib2')
@@ -2031,7 +2034,64 @@ Subclass ``QueueHandler``
delay = random.random() * 2 + 0.5
time.sleep(delay)
-You can run the above two snippets in separate command shells.
+You can run the above two snippets in separate command shells. If we run the
+listener in one shell and run the sender in two separate shells, we should see
+something like the following. In the first sender shell:
+
+.. code-block:: console
+
+$ python sender.py
+DEBUG myapp613 Message no. 1
+WARNING myapp.lib2613 Message no. 2
+CRITICAL myapp.lib2613 Message no. 3
+WARNING myapp.lib2613 Message no. 4
+CRITICAL myapp.lib1613 Message no. 5
+DEBUG myapp613 Message no. 6
+CRITICAL myapp.lib1613 Message no. 7
+INFO myapp.lib1613 Message no. 8
+(and so on)
+
+In the second sender shell:
+
+.. code-block:: console
+
+$ python sender.py
+INFO myapp.lib2657 Message no. 1
+CRITICAL myapp.lib2657 Message no. 2
+CRITICAL myapp657 Message no. 3
+CRITICAL myapp.lib1657 Message no. 4
+INFO myapp.lib1657 Message no. 5
+WARNING myapp.lib2657 Message no. 6
+CRITICAL myapp657 Message no. 7
+DEBUGmyapp.lib1657 Message no. 8
+(and so on)
+
+In the listener shell:
+
+.. code-block:: console
+
+$ python listener.py
+Press Ctrl-C to stop.
+DEBUG myapp613 Message no. 1
+WARNING myapp.lib2613 Message no. 2
+INFO myapp.lib2657 Message no. 1
+CRITICAL myapp.lib2613 Message no. 3
+CRITICAL myapp.lib2657 Message no. 2
+CRITICAL myapp657 Message no. 3
+WARNING myapp.lib2613 Message no. 4
+CRITICAL myapp.lib1613 Message no. 5
+CRITICAL myapp.lib1657 Message no. 4
+INFO myapp.lib1657 Message no. 5
+DEBUG myapp613 Message no. 6
+WARNING myapp.lib2657 Message no. 6
+CRITICAL myapp657 Message no. 7
+CRITICAL myapp.lib1613 Message no. 7
+INFO myapp.lib1613 Message no. 8
+DEBUGmyapp.lib1657 Message no. 8
+(and so on)
+
+As you can see, the logging from the two sender processes is interleaved in the
+listener's output.
An example dictionary-based configuration
diff --git a/Doc/library/logg
[Python-checkins] [3.12] [doc] Update logging documentation to improve grammar and elucidate an example. (GH-117541) (GH-117542)
https://github.com/python/cpython/commit/663e7bc2eed89c72e795d7b855c7ff283c0264c1 commit: 663e7bc2eed89c72e795d7b855c7ff283c0264c1 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-04-04T14:19:10+01:00 summary: [3.12] [doc] Update logging documentation to improve grammar and elucidate an example. (GH-117541) (GH-117542) (cherry picked from commit df912c913a3d94995b379f1e19fe0a79acab6169) files: M Doc/howto/logging-cookbook.rst M Doc/library/logging.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index fe13a7cbe8bcf9..d7b282a0de8aa6 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -1915,7 +1915,7 @@ In a similar way to the above section, we can implement a listener and handler using `pynng <https://pypi.org/project/pynng/>`_, which is a Python binding to `NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ. The following snippets illustrate -- you can test them in an environment which has -``pynng`` installed. Juat for variety, we present the listener first. +``pynng`` installed. Just for variety, we present the listener first. Subclass ``QueueListener`` @@ -1923,6 +1923,7 @@ Subclass ``QueueListener`` .. code-block:: python +# listener.py import json import logging import logging.handlers @@ -1955,7 +1956,7 @@ Subclass ``QueueListener`` break except pynng.Timeout: pass -except pynng.Closed: # sometimes hit when you hit Ctrl-C +except pynng.Closed: # sometimes happens when you hit Ctrl-C break if data is None: return None @@ -1988,6 +1989,7 @@ Subclass ``QueueHandler`` .. code-block:: python +# sender.py import json import logging import logging.handlers @@ -2015,9 +2017,10 @@ Subclass ``QueueHandler`` logging.getLogger('pynng').propagate = False handler = NNGSocketHandler(DEFAULT_ADDR) +# Make sure the process ID is in the output logging.basicConfig(level=logging.DEBUG, handlers=[logging.StreamHandler(), handler], -format='%(levelname)-8s %(name)10s %(message)s') +format='%(levelname)-8s %(name)10s %(process)6s %(message)s') levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) logger_names = ('myapp', 'myapp.lib1', 'myapp.lib2') @@ -2031,7 +2034,64 @@ Subclass ``QueueHandler`` delay = random.random() * 2 + 0.5 time.sleep(delay) -You can run the above two snippets in separate command shells. +You can run the above two snippets in separate command shells. If we run the +listener in one shell and run the sender in two separate shells, we should see +something like the following. In the first sender shell: + +.. code-block:: console + +$ python sender.py +DEBUG myapp613 Message no. 1 +WARNING myapp.lib2613 Message no. 2 +CRITICAL myapp.lib2613 Message no. 3 +WARNING myapp.lib2613 Message no. 4 +CRITICAL myapp.lib1613 Message no. 5 +DEBUG myapp613 Message no. 6 +CRITICAL myapp.lib1613 Message no. 7 +INFO myapp.lib1613 Message no. 8 +(and so on) + +In the second sender shell: + +.. code-block:: console + +$ python sender.py +INFO myapp.lib2657 Message no. 1 +CRITICAL myapp.lib2657 Message no. 2 +CRITICAL myapp657 Message no. 3 +CRITICAL myapp.lib1657 Message no. 4 +INFO myapp.lib1657 Message no. 5 +WARNING myapp.lib2657 Message no. 6 +CRITICAL myapp657 Message no. 7 +DEBUGmyapp.lib1657 Message no. 8 +(and so on) + +In the listener shell: + +.. code-block:: console + +$ python listener.py +Press Ctrl-C to stop. +DEBUG myapp613 Message no. 1 +WARNING myapp.lib2613 Message no. 2 +INFO myapp.lib2657 Message no. 1 +CRITICAL myapp.lib2613 Message no. 3 +CRITICAL myapp.lib2657 Message no. 2 +CRITICAL myapp657 Message no. 3 +WARNING myapp.lib2613 Message no. 4 +CRITICAL myapp.lib1613 Message no. 5 +CRITICAL myapp.lib1657 Message no. 4 +INFO myapp.lib1657 Message no. 5 +DEBUG myapp613 Message no. 6 +WARNING myapp.lib2657 Message no. 6 +CRITICAL myapp657 Message no. 7 +CRITICAL myapp.lib1613 Message no. 7 +INFO myapp.lib1613 Message no. 8 +DEBUGmyapp.lib1657 Message no. 8 +(and so on) + +As you can see, the
[Python-checkins] gh-102402: Fix floating point math issue by using `time.time_ns()` in `logging.LogRecord` (GH-102412)
https://github.com/python/cpython/commit/1316692e8c7c1e1f3b6639e51804f9db5ed892ea
commit: 1316692e8c7c1e1f3b6639e51804f9db5ed892ea
branch: main
author: Douglas Thor
committer: vsajip
date: 2024-04-16T10:44:57+01:00
summary:
gh-102402: Fix floating point math issue by using `time.time_ns()` in
`logging.LogRecord` (GH-102412)
files:
A Misc/NEWS.d/next/Library/2023-03-03-21-13-08.gh-issue-102402.fpkRO1.rst
M Doc/library/logging.rst
M Lib/logging/__init__.py
M Lib/test/test_logging.py
diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst
index 7816cc20945fa8..a733b288ecb6d0 100644
--- a/Doc/library/logging.rst
+++ b/Doc/library/logging.rst
@@ -1003,7 +1003,7 @@ the options available to you.
|| | portion of the time).
|
++-+---+
| created| ``%(created)f`` | Time when the :class:`LogRecord`
was created |
-|| | (as returned by
:func:`time.time`). |
+|| | (as returned by
:func:`time.time_ns` / 1e9). |
++-+---+
| exc_info | You shouldn't need to | Exception tuple (à la
``sys.exc_info``) or, |
|| format this yourself. | if no exception has occurred,
``None``. |
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index 927e3e653f065a..174b37c0ab305b 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -56,7 +56,7 @@
#
#_startTime is used as the base when calculating the relative time of events
#
-_startTime = time.time()
+_startTime = time.time_ns()
#
#raiseExceptions is used to see if exceptions during handling should be
@@ -300,7 +300,7 @@ def __init__(self, name, level, pathname, lineno,
"""
Initialize a logging record with interesting information.
"""
-ct = time.time()
+ct = time.time_ns()
self.name = name
self.msg = msg
#
@@ -339,9 +339,14 @@ def __init__(self, name, level, pathname, lineno,
self.stack_info = sinfo
self.lineno = lineno
self.funcName = func
-self.created = ct
-self.msecs = int((ct - int(ct)) * 1000) + 0.0 # see gh-89047
-self.relativeCreated = (self.created - _startTime) * 1000
+self.created = ct / 1e9 # ns to float seconds
+
+# Get the number of whole milliseconds (0-999) in the fractional part
of seconds.
+# Eg: 1_677_903_920_999_998_503 ns --> 999_998_503 ns--> 999 ms
+# Convert to float by adding 0.0 for historical reasons. See gh-89047
+self.msecs = (ct % 1_000_000_000) // 1_000_000 + 0.0
+
+self.relativeCreated = (ct - _startTime) / 1e6
if logThreads:
self.thread = threading.get_ident()
self.threadName = threading.current_thread().name
@@ -572,7 +577,7 @@ class Formatter(object):
%(lineno)d Source line number where the logging call was issued
(if available)
%(funcName)sFunction name
-%(created)f Time when the LogRecord was created (time.time()
+%(created)f Time when the LogRecord was created (time.time_ns() /
1e9
return value)
%(asctime)s Textual time when the LogRecord was created
%(msecs)d Millisecond portion of the creation time
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index c3c4de06fa0f09..3f0b363066df2c 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -60,6 +60,7 @@
import weakref
from http.server import HTTPServer, BaseHTTPRequestHandler
+from unittest.mock import patch
from urllib.parse import urlparse, parse_qs
from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
ThreadingTCPServer, StreamRequestHandler)
@@ -4552,6 +4553,44 @@ def test_issue_89047(self):
s = f.format(r)
self.assertNotIn('.1000', s)
+def test_msecs_has_no_floating_point_precision_loss(self):
+# See issue gh-102402
+tests = (
+# time_ns is approx. 2023-03-04 04:25:20 UTC
+# (time_ns, expected_msecs_value)
+(1_677_902_297_100_000_000, 100.0), # exactly 100ms
+(1_677_903_920_999_998_503, 999.0), # check truncating doesn't
round
+(1_677_903_920_000_998_503, 0.0), # check truncating doesn't round
+)
+for ns, want in tests:
+with patch('time.time_ns') as patched_ns:
+patched_ns.return_value = ns
+record = logging.makeLogRecord({'msg': 'test'})
+self.assertE
[Python-checkins] gh-117975: Ensure flush level is checked when configuring a logging MemoryHandler. (GH-117976)
https://github.com/python/cpython/commit/6d0bb43232dd6ebc5245daa4fe29f07f815f0bad
commit: 6d0bb43232dd6ebc5245daa4fe29f07f815f0bad
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-04-17T13:55:18+01:00
summary:
gh-117975: Ensure flush level is checked when configuring a logging
MemoryHandler. (GH-117976)
files:
M Lib/logging/config.py
M Lib/test/test_logging.py
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index ea37dd7544564a..860e4751207470 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -761,18 +761,20 @@ def configure_handler(self, config):
klass = cname
else:
klass = self.resolve(cname)
-if issubclass(klass, logging.handlers.MemoryHandler) and\
-'target' in config:
-# Special case for handler which refers to another handler
-try:
-tn = config['target']
-th = self.config['handlers'][tn]
-if not isinstance(th, logging.Handler):
-config.update(config_copy) # restore for deferred cfg
-raise TypeError('target not configured yet')
-config['target'] = th
-except Exception as e:
-raise ValueError('Unable to set target handler %r' % tn)
from e
+if issubclass(klass, logging.handlers.MemoryHandler):
+if 'flushLevel' in config:
+config['flushLevel'] =
logging._checkLevel(config['flushLevel'])
+if 'target' in config:
+# Special case for handler which refers to another handler
+try:
+tn = config['target']
+th = self.config['handlers'][tn]
+if not isinstance(th, logging.Handler):
+config.update(config_copy) # restore for deferred
cfg
+raise TypeError('target not configured yet')
+config['target'] = th
+except Exception as e:
+raise ValueError('Unable to set target handler %r' %
tn) from e
elif issubclass(klass, logging.handlers.QueueHandler):
# Another special case for handler which refers to other
handlers
# if 'handlers' not in config:
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 3f0b363066df2c..826b7381686dcb 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3036,6 +3036,30 @@ def format(self, record):
},
}
+config18 = {
+"version": 1,
+"handlers": {
+"console": {
+"class": "logging.StreamHandler",
+"level": "DEBUG",
+},
+"buffering": {
+"class": "logging.handlers.MemoryHandler",
+"capacity": 5,
+"target": "console",
+"level": "DEBUG",
+"flushLevel": "ERROR"
+}
+},
+"loggers": {
+"mymodule": {
+"level": "DEBUG",
+"handlers": ["buffering"],
+"propagate": "true"
+}
+}
+}
+
bad_format = {
"version": 1,
"formatters": {
@@ -3522,6 +3546,11 @@ def test_config17_ok(self):
h = logging._handlers['hand1']
self.assertEqual(h.formatter.custom_property, 'value')
+def test_config18_ok(self):
+self.apply_config(self.config18)
+handler = logging.getLogger('mymodule').handlers[0]
+self.assertEqual(handler.flushLevel, logging.ERROR)
+
def setup_via_listener(self, text, verify=None):
text = text.encode("utf-8")
# Ask for a randomly assigned port (by using port 0)
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-117975: Ensure flush level is checked when configuring a logging MemoryHandler. (GH-117976) (GH-117986)
https://github.com/python/cpython/commit/2b68c81283a8a7d2e09f9ca774048181c872471b commit: 2b68c81283a8a7d2e09f9ca774048181c872471b branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-04-17T14:48:09+01:00 summary: [3.12] gh-117975: Ensure flush level is checked when configuring a logging MemoryHandler. (GH-117976) (GH-117986) (cherry picked from commit 6d0bb43232dd6ebc5245daa4fe29f07f815f0bad) files: M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 33417b75d51935..2c0d30ccf80692 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -768,18 +768,20 @@ def configure_handler(self, config): klass = cname else: klass = self.resolve(cname) -if issubclass(klass, logging.handlers.MemoryHandler) and\ -'target' in config: -# Special case for handler which refers to another handler -try: -tn = config['target'] -th = self.config['handlers'][tn] -if not isinstance(th, logging.Handler): -config.update(config_copy) # restore for deferred cfg -raise TypeError('target not configured yet') -config['target'] = th -except Exception as e: -raise ValueError('Unable to set target handler %r' % tn) from e +if issubclass(klass, logging.handlers.MemoryHandler): +if 'flushLevel' in config: +config['flushLevel'] = logging._checkLevel(config['flushLevel']) +if 'target' in config: +# Special case for handler which refers to another handler +try: +tn = config['target'] +th = self.config['handlers'][tn] +if not isinstance(th, logging.Handler): +config.update(config_copy) # restore for deferred cfg +raise TypeError('target not configured yet') +config['target'] = th +except Exception as e: +raise ValueError('Unable to set target handler %r' % tn) from e elif issubclass(klass, logging.handlers.QueueHandler): # Another special case for handler which refers to other handlers # if 'handlers' not in config: diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index a4b2b4f9c8fd40..cf9fd427be2f91 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -3050,6 +3050,30 @@ def format(self, record): }, } +config18 = { +"version": 1, +"handlers": { +"console": { +"class": "logging.StreamHandler", +"level": "DEBUG", +}, +"buffering": { +"class": "logging.handlers.MemoryHandler", +"capacity": 5, +"target": "console", +"level": "DEBUG", +"flushLevel": "ERROR" +} +}, +"loggers": { +"mymodule": { +"level": "DEBUG", +"handlers": ["buffering"], +"propagate": "true" +} +} +} + bad_format = { "version": 1, "formatters": { @@ -3536,6 +3560,11 @@ def test_config17_ok(self): h = logging._handlers['hand1'] self.assertEqual(h.formatter.custom_property, 'value') +def test_config18_ok(self): +self.apply_config(self.config18) +handler = logging.getLogger('mymodule').handlers[0] +self.assertEqual(handler.flushLevel, logging.ERROR) + def setup_via_listener(self, text, verify=None): text = text.encode("utf-8") # Ask for a randomly assigned port (by using port 0) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] Docs: fix typos in documentation (GH-118815)
https://github.com/python/cpython/commit/17a2cc199d5d8dd9db49ea596cf243e2217263c6 commit: 17a2cc199d5d8dd9db49ea596cf243e2217263c6 branch: main author: Xie Yanbo committer: vsajip date: 2024-05-10T09:11:50+01:00 summary: Docs: fix typos in documentation (GH-118815) files: M Doc/library/importlib.rst M Doc/library/logging.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index b58ef359378e4f..2ec15dd171c18a 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -1252,7 +1252,7 @@ find and load modules. be only a single binary per framework, and there can be no executable binary material outside the Frameworks folder. - To accomodate this requirement, when running on iOS, extension module + To accommodate this requirement, when running on iOS, extension module binaries are *not* packaged as ``.so`` files on ``sys.path``, but as individual standalone frameworks. To discover those frameworks, this loader is be registered against the ``.fwork`` file extension, with a ``.fwork`` @@ -1279,7 +1279,7 @@ find and load modules. When a module is loaded with this loader, the ``__file__`` for the module will report as the location of the ``.fwork`` file. This allows code to use - the ``__file__`` of a module as an anchor for file system traveral. + the ``__file__`` of a module as an anchor for file system traversal. However, the spec origin will reference the location of the *actual* binary in the ``.framework`` folder. diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index fb6ca38ba72aba..564b34bcf1bb37 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -1204,7 +1204,7 @@ functions. most programs will want to carefully and explicitly control the logging configuration, and should therefore prefer creating a module-level logger and calling :meth:`Logger.debug` (or other level-specific methods) on it, as - described at the beginnning of this documentation. + described at the beginning of this documentation. .. function:: info(msg, *args, **kwargs) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-119078: Clarify venv tutorial (GH-119129)
https://github.com/python/cpython/commit/0f5e8bed636c2f29701e5a1965d1b088d33abbf0 commit: 0f5e8bed636c2f29701e5a1965d1b088d33abbf0 branch: main author: Nice Zombies committer: vsajip date: 2024-05-18T12:44:02+01:00 summary: gh-119078: Clarify venv tutorial (GH-119129) files: M Doc/tutorial/venv.rst diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst index a6dead2eac11f6..6cca3f1b25aadc 100644 --- a/Doc/tutorial/venv.rst +++ b/Doc/tutorial/venv.rst @@ -36,10 +36,10 @@ Creating Virtual Environments = The module used to create and manage virtual environments is called -:mod:`venv`. :mod:`venv` will usually install the most recent version of -Python that you have available. If you have multiple versions of Python on your -system, you can select a specific Python version by running ``python3`` or -whichever version you want. +:mod:`venv`. :mod:`venv` will install the Python version from which +the command was run (as reported by the :option:`--version` option). +For instance, excuting the command with ``python3.12`` will install +version 3.12. To create a virtual environment, decide upon a directory where you want to place it, and run the :mod:`venv` module as a script with the directory path:: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-119078: Clarify venv tutorial (GH-119129) (GH-119141)
https://github.com/python/cpython/commit/bb44a827f132e4c2c75cda125bc4741ce5d1440c commit: bb44a827f132e4c2c75cda125bc4741ce5d1440c branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-05-18T12:54:40+01:00 summary: [3.12] gh-119078: Clarify venv tutorial (GH-119129) (GH-119141) (cherry picked from commit 0f5e8bed636c2f29701e5a1965d1b088d33abbf0) files: M Doc/tutorial/venv.rst diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst index a6dead2eac11f6..6cca3f1b25aadc 100644 --- a/Doc/tutorial/venv.rst +++ b/Doc/tutorial/venv.rst @@ -36,10 +36,10 @@ Creating Virtual Environments = The module used to create and manage virtual environments is called -:mod:`venv`. :mod:`venv` will usually install the most recent version of -Python that you have available. If you have multiple versions of Python on your -system, you can select a specific Python version by running ``python3`` or -whichever version you want. +:mod:`venv`. :mod:`venv` will install the Python version from which +the command was run (as reported by the :option:`--version` option). +For instance, excuting the command with ``python3.12`` will install +version 3.12. To create a virtual environment, decide upon a directory where you want to place it, and run the :mod:`venv` module as a script with the directory path:: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-119078: Clarify venv tutorial (GH-119129) (GH-119142)
https://github.com/python/cpython/commit/1bb3a9df1b0e4884c1bc6316eb1275ac17a2cedc commit: 1bb3a9df1b0e4884c1bc6316eb1275ac17a2cedc branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-05-18T13:06:23+01:00 summary: [3.13] gh-119078: Clarify venv tutorial (GH-119129) (GH-119142) (cherry picked from commit 0f5e8bed636c2f29701e5a1965d1b088d33abbf0) files: M Doc/tutorial/venv.rst diff --git a/Doc/tutorial/venv.rst b/Doc/tutorial/venv.rst index a6dead2eac11f6..6cca3f1b25aadc 100644 --- a/Doc/tutorial/venv.rst +++ b/Doc/tutorial/venv.rst @@ -36,10 +36,10 @@ Creating Virtual Environments = The module used to create and manage virtual environments is called -:mod:`venv`. :mod:`venv` will usually install the most recent version of -Python that you have available. If you have multiple versions of Python on your -system, you can select a specific Python version by running ``python3`` or -whichever version you want. +:mod:`venv`. :mod:`venv` will install the Python version from which +the command was run (as reported by the :option:`--version` option). +For instance, excuting the command with ``python3.12`` will install +version 3.12. To create a virtual environment, decide upon a directory where you want to place it, and run the :mod:`venv` module as a script with the directory path:: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [doc] Clarify the nature of the root logger in the `logging` documentation (GH-119440)
https://github.com/python/cpython/commit/b407ad38fb93585332c370b8fa56905fb238cdfd commit: b407ad38fb93585332c370b8fa56905fb238cdfd branch: main author: Justin Kunimune committer: vsajip date: 2024-05-28T11:31:20+01:00 summary: [doc] Clarify the nature of the root logger in the `logging` documentation (GH-119440) Co-authored-by: Vinay Sajip files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 564b34bcf1bb37..4ba520c139ebce 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -109,11 +109,11 @@ The ``name`` is potentially a period-separated hierarchical value, like Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of ``foo``, loggers with names of ``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all -descendants of ``foo``. The logger name hierarchy is analogous to the Python -package hierarchy, and identical to it if you organise your loggers on a -per-module basis using the recommended construction -``logging.getLogger(__name__)``. That's because in a module, ``__name__`` -is the module's name in the Python package namespace. +descendants of ``foo``. In addition, all loggers are descendants of the root +logger. The logger name hierarchy is analogous to the Python package hierarchy, +and identical to it if you organise your loggers on a per-module basis using +the recommended construction ``logging.getLogger(__name__)``. That's because +in a module, ``__name__`` is the module's name in the Python package namespace. .. class:: Logger @@ -1157,10 +1157,12 @@ functions. .. function:: getLogger(name=None) - Return a logger with the specified name or, if name is ``None``, return a - logger which is the root logger of the hierarchy. If specified, the name is - typically a dot-separated hierarchical name like *'a'*, *'a.b'* or *'a.b.c.d'*. - Choice of these names is entirely up to the developer who is using logging. + Return a logger with the specified name or, if name is ``None``, return the + root logger of the hierarchy. If specified, the name is typically a + dot-separated hierarchical name like *'a'*, *'a.b'* or *'a.b.c.d'*. Choice + of these names is entirely up to the developer who is using logging, though + it is recommended that ``__name__`` be used unless you have a specific + reason for not doing that, as mentioned in :ref:`logger`. All calls to this function with a given name return the same logger instance. This means that logger instances never need to be passed between different parts ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] [doc] Clarify the nature of the root logger in the `logging` documentation (GH-119440) (GH-119652)
https://github.com/python/cpython/commit/5cca0419cf3778a6b3a4f7f67147437ba8c84dfe commit: 5cca0419cf3778a6b3a4f7f67147437ba8c84dfe branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-05-28T15:04:19+01:00 summary: [3.12] [doc] Clarify the nature of the root logger in the `logging` documentation (GH-119440) (GH-119652) (cherry picked from commit b407ad38fb93585332c370b8fa56905fb238cdfd) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 76ecad5c5aa3c8..c6ab3744ad5338 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -109,11 +109,11 @@ The ``name`` is potentially a period-separated hierarchical value, like Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of ``foo``, loggers with names of ``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all -descendants of ``foo``. The logger name hierarchy is analogous to the Python -package hierarchy, and identical to it if you organise your loggers on a -per-module basis using the recommended construction -``logging.getLogger(__name__)``. That's because in a module, ``__name__`` -is the module's name in the Python package namespace. +descendants of ``foo``. In addition, all loggers are descendants of the root +logger. The logger name hierarchy is analogous to the Python package hierarchy, +and identical to it if you organise your loggers on a per-module basis using +the recommended construction ``logging.getLogger(__name__)``. That's because +in a module, ``__name__`` is the module's name in the Python package namespace. .. class:: Logger @@ -1144,10 +1144,12 @@ functions. .. function:: getLogger(name=None) - Return a logger with the specified name or, if name is ``None``, return a - logger which is the root logger of the hierarchy. If specified, the name is - typically a dot-separated hierarchical name like *'a'*, *'a.b'* or *'a.b.c.d'*. - Choice of these names is entirely up to the developer who is using logging. + Return a logger with the specified name or, if name is ``None``, return the + root logger of the hierarchy. If specified, the name is typically a + dot-separated hierarchical name like *'a'*, *'a.b'* or *'a.b.c.d'*. Choice + of these names is entirely up to the developer who is using logging, though + it is recommended that ``__name__`` be used unless you have a specific + reason for not doing that, as mentioned in :ref:`logger`. All calls to this function with a given name return the same logger instance. This means that logger instances never need to be passed between different parts ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] [doc] Clarify the nature of the root logger in the `logging` documentation (GH-119440) (GH-119651)
https://github.com/python/cpython/commit/47fb0f82cdd949b863e6e8dda13e38c62d6509cb commit: 47fb0f82cdd949b863e6e8dda13e38c62d6509cb branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-05-28T15:04:46+01:00 summary: [3.13] [doc] Clarify the nature of the root logger in the `logging` documentation (GH-119440) (GH-119651) (cherry picked from commit b407ad38fb93585332c370b8fa56905fb238cdfd) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index fb6ca38ba72aba..0624423c950797 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -109,11 +109,11 @@ The ``name`` is potentially a period-separated hierarchical value, like Loggers that are further down in the hierarchical list are children of loggers higher up in the list. For example, given a logger with a name of ``foo``, loggers with names of ``foo.bar``, ``foo.bar.baz``, and ``foo.bam`` are all -descendants of ``foo``. The logger name hierarchy is analogous to the Python -package hierarchy, and identical to it if you organise your loggers on a -per-module basis using the recommended construction -``logging.getLogger(__name__)``. That's because in a module, ``__name__`` -is the module's name in the Python package namespace. +descendants of ``foo``. In addition, all loggers are descendants of the root +logger. The logger name hierarchy is analogous to the Python package hierarchy, +and identical to it if you organise your loggers on a per-module basis using +the recommended construction ``logging.getLogger(__name__)``. That's because +in a module, ``__name__`` is the module's name in the Python package namespace. .. class:: Logger @@ -1157,10 +1157,12 @@ functions. .. function:: getLogger(name=None) - Return a logger with the specified name or, if name is ``None``, return a - logger which is the root logger of the hierarchy. If specified, the name is - typically a dot-separated hierarchical name like *'a'*, *'a.b'* or *'a.b.c.d'*. - Choice of these names is entirely up to the developer who is using logging. + Return a logger with the specified name or, if name is ``None``, return the + root logger of the hierarchy. If specified, the name is typically a + dot-separated hierarchical name like *'a'*, *'a.b'* or *'a.b.c.d'*. Choice + of these names is entirely up to the developer who is using logging, though + it is recommended that ``__name__`` be used unless you have a specific + reason for not doing that, as mentioned in :ref:`logger`. All calls to this function with a given name return the same logger instance. This means that logger instances never need to be passed between different parts ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-113692: skip a test if multiprocessing isn't available. (GH-113704)
https://github.com/python/cpython/commit/842b738129021f52293dc053e014ecb4fe095baa
commit: 842b738129021f52293dc053e014ecb4fe095baa
branch: main
author: Vinay Sajip
committer: vsajip
date: 2024-01-09T07:47:42Z
summary:
gh-113692: skip a test if multiprocessing isn't available. (GH-113704)
files:
M Lib/test/test_logging.py
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 33db5d6443f6fd..0be26981184213 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -43,6 +43,7 @@
import tempfile
from test.support.script_helper import assert_python_ok, assert_python_failure
from test import support
+from test.support import import_helper
from test.support import os_helper
from test.support import socket_helper
from test.support import threading_helper
@@ -3924,7 +3925,8 @@ def test_90195(self):
def test_111615(self):
# See gh-111615
-import multiprocessing as mp
+import_helper.import_module('_multiprocessing') # see gh-113692
+mp = import_helper.import_module('multiprocessing')
config = {
'version': 1,
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] [3.12] gh-113692: skip a test if multiprocessing isn't available. (GH-113704) (GH-113844)
https://github.com/python/cpython/commit/cdd703d1313b546823882663ca94cf536f025f4a commit: cdd703d1313b546823882663ca94cf536f025f4a branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-01-09T08:13:41Z summary: [3.12] gh-113692: skip a test if multiprocessing isn't available. (GH-113704) (GH-113844) (cherry picked from commit 842b738129021f52293dc053e014ecb4fe095baa) files: M Lib/test/test_logging.py diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index df08ccd362a073..635dd7c26f6eed 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -43,6 +43,7 @@ import tempfile from test.support.script_helper import assert_python_ok, assert_python_failure from test import support +from test.support import import_helper from test.support import os_helper from test.support import socket_helper from test.support import threading_helper @@ -3894,7 +3895,8 @@ def test_90195(self): def test_111615(self): # See gh-111615 -import multiprocessing as mp +import_helper.import_module('_multiprocessing') # see gh-113692 +mp = import_helper.import_module('multiprocessing') config = { 'version': 1, ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] Trivial change: Update comments in activate about what running hash -r does (GH-125385) (#125388)
https://github.com/python/cpython/commit/77020dae341764ee440c99cf8ba13c7766730b46 commit: 77020dae341764ee440c99cf8ba13c7766730b46 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-13T08:55:37+01:00 summary: [3.12] Trivial change: Update comments in activate about what running hash -r does (GH-125385) (#125388) (cherry picked from commit 82bcaf15890cf85b76b4f62d2dd1710bb49c3ed1) files: M Lib/venv/scripts/common/activate diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index d5914e0cbb40d5..44df44a7404b43 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -14,8 +14,9 @@ deactivate () { unset _OLD_VIRTUAL_PYTHONHOME fi -# Call hash to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected +# Call hash to forget past locations. Without forgetting +# past locations the $PATH changes we made may not be respected. +# See "man bash" for more details. hash is usually a builtin of your shell hash -r 2> /dev/null if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] Trivial change: Update comments in activate about what running hash -r does (GH-125385) (GH-125387)
https://github.com/python/cpython/commit/801ef7de08d816b1976865496e834c5af941a8ad commit: 801ef7de08d816b1976865496e834c5af941a8ad branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-13T08:56:09+01:00 summary: [3.13] Trivial change: Update comments in activate about what running hash -r does (GH-125385) (GH-125387) (cherry picked from commit 82bcaf15890cf85b76b4f62d2dd1710bb49c3ed1) files: M Lib/venv/scripts/common/activate diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index cbd4873f012246..4593799b7e9b0e 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -14,8 +14,9 @@ deactivate () { unset _OLD_VIRTUAL_PYTHONHOME fi -# Call hash to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected +# Call hash to forget past locations. Without forgetting +# past locations the $PATH changes we made may not be respected. +# See "man bash" for more details. hash is usually a builtin of your shell hash -r 2> /dev/null if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] Trivial change: Update comments in activate about what running hash -r does (GH-125385)
https://github.com/python/cpython/commit/82bcaf15890cf85b76b4f62d2dd1710bb49c3ed1 commit: 82bcaf15890cf85b76b4f62d2dd1710bb49c3ed1 branch: main author: Andrew Athan <[email protected]> committer: vsajip date: 2024-10-13T08:22:05+01:00 summary: Trivial change: Update comments in activate about what running hash -r does (GH-125385) Update comments about what running hash -r does The old comment said "hash -r" forgets "past commands." However, the documentation for "hash" states that it forgets past locations. The old comment was, in my opinion, confusing. This is because it could be interpreted to mean it does something to the command history (HISTORY/HISTFILE etc) vs the cache of locations. files: M Lib/venv/scripts/common/activate diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index cbd4873f012246..4593799b7e9b0e 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -14,8 +14,9 @@ deactivate () { unset _OLD_VIRTUAL_PYTHONHOME fi -# Call hash to forget past commands. Without forgetting -# past commands the $PATH changes we made may not be respected +# Call hash to forget past locations. Without forgetting +# past locations the $PATH changes we made may not be respected. +# See "man bash" for more details. hash is usually a builtin of your shell hash -r 2> /dev/null if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] docs: in venv table use executable name (GH-124315) (GH-125172)
https://github.com/python/cpython/commit/47fa3f44ef30d0d677415e8903032841c1e1d3b7 commit: 47fa3f44ef30d0d677415e8903032841c1e1d3b7 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-09T17:54:15+01:00 summary: [3.13] docs: in venv table use executable name (GH-124315) (GH-125172) (cherry picked from commit 7f93dbf6fec152888727a0f25a3aa030d1fe27ca) files: M Doc/library/venv.rst diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index e2c77963ff3040..5205c6c211d9bf 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -215,7 +215,7 @@ containing the virtual environment): | ++--+ | | csh/tcsh | :samp:`$ source {}/bin/activate.csh` | | ++--+ -| | PowerShell | :samp:`$ {}/bin/Activate.ps1` | +| | pwsh | :samp:`$ {}/bin/Activate.ps1` | +-++--+ | Windows | cmd.exe| :samp:`C:\\> {}\\Scripts\\activate.bat`| | ++--+ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] docs: in venv table use executable name (GH-124315) (#125171)
https://github.com/python/cpython/commit/046687c620f5bff8fc9dead066ab30e9802ba5b0 commit: 046687c620f5bff8fc9dead066ab30e9802ba5b0 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-09T17:54:38+01:00 summary: [3.12] docs: in venv table use executable name (GH-124315) (#125171) (cherry picked from commit 7f93dbf6fec152888727a0f25a3aa030d1fe27ca) files: M Doc/library/venv.rst diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index 546ce3716d28b9..9eb0e9d191c7cd 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -205,7 +205,7 @@ containing the virtual environment): | ++--+ | | csh/tcsh | :samp:`$ source {}/bin/activate.csh` | | ++--+ -| | PowerShell | :samp:`$ {}/bin/Activate.ps1` | +| | pwsh | :samp:`$ {}/bin/Activate.ps1` | +-++--+ | Windows | cmd.exe| :samp:`C:\\> {}\\Scripts\\activate.bat`| | ++--+ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] docs: in venv table use executable name (GH-124315)
https://github.com/python/cpython/commit/7f93dbf6fec152888727a0f25a3aa030d1fe27ca commit: 7f93dbf6fec152888727a0f25a3aa030d1fe27ca branch: main author: musvaage <[email protected]> committer: vsajip date: 2024-10-09T09:04:35+01:00 summary: docs: in venv table use executable name (GH-124315) Co-authored-by: musvaage files: M Doc/library/venv.rst diff --git a/Doc/library/venv.rst b/Doc/library/venv.rst index e2c77963ff3040..5205c6c211d9bf 100644 --- a/Doc/library/venv.rst +++ b/Doc/library/venv.rst @@ -215,7 +215,7 @@ containing the virtual environment): | ++--+ | | csh/tcsh | :samp:`$ source {}/bin/activate.csh` | | ++--+ -| | PowerShell | :samp:`$ {}/bin/Activate.ps1` | +| | pwsh | :samp:`$ {}/bin/Activate.ps1` | +-++--+ | Windows | cmd.exe| :samp:`C:\\> {}\\Scripts\\activate.bat`| | ++--+ ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897)
https://github.com/python/cpython/commit/7ffe94fb242fd51bb07c7f0d31e94efeea3619d4 commit: 7ffe94fb242fd51bb07c7f0d31e94efeea3619d4 branch: main author: Bénédikt Tran <[email protected]> committer: vsajip date: 2024-10-07T19:42:19+01:00 summary: gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) files: A Misc/NEWS.d/next/Library/2024-10-02-15-05-45.gh-issue-124653.tqsTu9.rst M Doc/library/logging.config.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 317ca8728248c8..0e9dc33ae2123a 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -753,16 +753,17 @@ The ``queue`` and ``listener`` keys are optional. If the ``queue`` key is present, the corresponding value can be one of the following: -* An object implementing the :class:`queue.Queue` public API. For instance, - this may be an actual instance of :class:`queue.Queue` or a subclass thereof, - or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. +* An object implementing the :meth:`Queue.put_nowait ` + and :meth:`Queue.get ` public API. For instance, this may be + an actual instance of :class:`queue.Queue` or a subclass thereof, or a proxy + obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. This is of course only possible if you are constructing or modifying the configuration dictionary in code. * A string that resolves to a callable which, when called with no arguments, returns - the :class:`queue.Queue` instance to use. That callable could be a - :class:`queue.Queue` subclass or a function which returns a suitable queue instance, + the queue instance to use. That callable could be a :class:`queue.Queue` subclass + or a function which returns a suitable queue instance, such as ``my.module.queue_factory()``. * A dict with a ``'()'`` key which is constructed in the usual way as discussed in diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 3781cb1aeb9ae2..6a6a7f726f7e0c 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -499,7 +499,7 @@ def as_tuple(self, value): def _is_queue_like_object(obj): """Check that *obj* implements the Queue API.""" -if isinstance(obj, queue.Queue): +if isinstance(obj, (queue.Queue, queue.SimpleQueue)): return True # defer importing multiprocessing as much as possible from multiprocessing.queues import Queue as MPQueue @@ -516,13 +516,13 @@ def _is_queue_like_object(obj): # Ideally, we would have wanted to simply use strict type checking # instead of a protocol-based type checking since the latter does # not check the method signatures. -queue_interface = [ -'empty', 'full', 'get', 'get_nowait', -'put', 'put_nowait', 'join', 'qsize', -'task_done', -] +# +# Note that only 'put_nowait' and 'get' are required by the logging +# queue handler and queue listener (see gh-124653) and that other +# methods are either optional or unused. +minimal_queue_interface = ['put_nowait', 'get'] return all(callable(getattr(obj, method, None)) - for method in queue_interface) + for method in minimal_queue_interface) class DictConfigurator(BaseConfigurator): """ diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 230ba954cd286d..d4ceb7c8dc0b41 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -2377,16 +2377,22 @@ def __getattr__(self, attribute): return getattr(queue, attribute) class CustomQueueFakeProtocol(CustomQueueProtocol): -# An object implementing the Queue API (incorrect signatures). +# An object implementing the minimial Queue API for +# the logging module but with incorrect signatures. +# # The object will be considered a valid queue class since we # do not check the signatures (only callability of methods) # but will NOT be usable in production since a TypeError will -# be raised due to a missing argument. -def empty(self, x): +# be raised due to the extra argument in 'put_nowait'. +def put_nowait(self): pass class CustomQueueWrongProtocol(CustomQueueProtocol): -empty = None +put_nowait = None + +class MinimalQueueProtocol: +def put_nowait(self, x): pass +def get(self): pass def queueMaker(): return queue.Queue() @@ -3946,56 +3952,70 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'") +def _apply_simple_queue_listener_configuration(self, qspec
[Python-checkins] gh-125398: Convert paths in venv activate script when using Git Bash under Windows (GH-125399)
https://github.com/python/cpython/commit/2a378dba987e125521b678364f0cd44b92dd5d52 commit: 2a378dba987e125521b678364f0cd44b92dd5d52 branch: main author: Julien committer: vsajip date: 2024-10-19T18:34:41+01:00 summary: gh-125398: Convert paths in venv activate script when using Git Bash under Windows (GH-125399) * Convert paths in venv activate script when using Git Bash under Windows With https://github.com/python/cpython/pull/112508 the check to converts paths when running on Windows was changed from using the non-posix environment variable `$OSTYPE` to using `uname` instead. However this missed the fact that when running under Git Bash on Windows, uname reports `MINGW*` (`$OSTYPE` is still `msys`). This results in `$PATH` being set to something like `D:\a\github-actions-shells\github-actions-shells\venv/Scripts:…`, instead of `/d/a/github-actions-shells/github-actions-shells/venv/Scripts`. Notably, the Git Bash is the bash shell that’s used for GitHub Actions Windows runners, and ships with VSCode. files: A Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst M Lib/venv/scripts/common/activate diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index 4593799b7e9b0e..44f137672e9d2e 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -38,8 +38,8 @@ deactivate nondestructive # on Windows, a path can contain colons and backslashes and has to be converted: case "$(uname)" in -CYGWIN*|MSYS*) -# transform D:\path\to\venv to /d/path/to/venv on MSYS +CYGWIN*|MSYS*|MINGW*) +# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW # and to /cygdrive/d/path/to/venv on Cygwin VIRTUAL_ENV=$(cygpath "__VENV_DIR__") export VIRTUAL_ENV diff --git a/Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst b/Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst new file mode 100644 index 00..a188b35e1fbdbc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst @@ -0,0 +1 @@ +Fix the conversion of the :envvar:`!VIRTUAL_ENV` path in the activate script in :mod:`venv` when running in Git Bash for Windows. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-125398: Convert paths in venv activate script when using Git Bash under Windows (GH-125399) (GH-125733)
https://github.com/python/cpython/commit/9e433304c42272130b5395c8138740ad1d79d0d7 commit: 9e433304c42272130b5395c8138740ad1d79d0d7 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-19T19:00:07+01:00 summary: [3.13] gh-125398: Convert paths in venv activate script when using Git Bash under Windows (GH-125399) (GH-125733) (cherry picked from commit 2a378dba987e125521b678364f0cd44b92dd5d52) files: A Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst M Lib/venv/scripts/common/activate diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate index 4593799b7e9b0e..44f137672e9d2e 100644 --- a/Lib/venv/scripts/common/activate +++ b/Lib/venv/scripts/common/activate @@ -38,8 +38,8 @@ deactivate nondestructive # on Windows, a path can contain colons and backslashes and has to be converted: case "$(uname)" in -CYGWIN*|MSYS*) -# transform D:\path\to\venv to /d/path/to/venv on MSYS +CYGWIN*|MSYS*|MINGW*) +# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW # and to /cygdrive/d/path/to/venv on Cygwin VIRTUAL_ENV=$(cygpath "__VENV_DIR__") export VIRTUAL_ENV diff --git a/Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst b/Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst new file mode 100644 index 00..a188b35e1fbdbc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-10-13-15-04-58.gh-issue-125398.UW7Ndv.rst @@ -0,0 +1 @@ +Fix the conversion of the :envvar:`!VIRTUAL_ENV` path in the activate script in :mod:`venv` when running in Git Bash for Windows. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-124651: Quote template strings in `venv` activation scripts (GH-124712)
https://github.com/python/cpython/commit/d48cc82ed25e26b02eb97c6263d95dcaa1e9111b commit: d48cc82ed25e26b02eb97c6263d95dcaa1e9111b branch: main author: Y5 <[email protected]> committer: vsajip date: 2024-10-21T21:48:04+01:00 summary: gh-124651: Quote template strings in `venv` activation scripts (GH-124712) This patch properly quotes template strings in `venv` activation scripts. This mitigates potential command injection. files: A Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst M Lib/test/test_venv.py M Lib/venv/__init__.py M Lib/venv/scripts/common/activate M Lib/venv/scripts/common/activate.fish M Lib/venv/scripts/nt/activate.bat M Lib/venv/scripts/posix/activate.csh diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 1ef08da326c18c..6b2127bd31e40a 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -17,6 +17,7 @@ import sys import sysconfig import tempfile +import shlex from test.support import (captured_stdout, captured_stderr, skip_if_broken_multiprocessing_synchronize, verbose, requires_subprocess, is_android, is_apple_mobile, @@ -110,6 +111,10 @@ def get_text_file_contents(self, *args, encoding='utf-8'): result = f.read() return result +def assertEndsWith(self, string, tail): +if not string.endswith(tail): +self.fail(f"String {string!r} does not end with {tail!r}") + class BasicTest(BaseTest): """Test venv module functionality.""" @@ -488,6 +493,82 @@ def test_executable_symlinks(self): 'import sys; print(sys.executable)']) self.assertEqual(out.strip(), envpy.encode()) +# gh-124651: test quoted strings [email protected](os.name == 'nt', 'contains invalid characters on Windows') +def test_special_chars_bash(self): +""" +Test that the template strings are quoted properly (bash) +""" +rmtree(self.env_dir) +bash = shutil.which('bash') +if bash is None: +self.skipTest('bash required for this test') +env_name = '"\';&&$e|\'"' +env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) +builder = venv.EnvBuilder(clear=True) +builder.create(env_dir) +activate = os.path.join(env_dir, self.bindir, 'activate') +test_script = os.path.join(self.env_dir, 'test_special_chars.sh') +with open(test_script, "w") as f: +f.write(f'source {shlex.quote(activate)}\n' +'python -c \'import sys; print(sys.executable)\'\n' +'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' +'deactivate\n') +out, err = check_output([bash, test_script]) +lines = out.splitlines() +self.assertTrue(env_name.encode() in lines[0]) +self.assertEndsWith(lines[1], env_name.encode()) + +# gh-124651: test quoted strings [email protected](os.name == 'nt', 'contains invalid characters on Windows') +def test_special_chars_csh(self): +""" +Test that the template strings are quoted properly (csh) +""" +rmtree(self.env_dir) +csh = shutil.which('tcsh') or shutil.which('csh') +if csh is None: +self.skipTest('csh required for this test') +env_name = '"\';&&$e|\'"' +env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) +builder = venv.EnvBuilder(clear=True) +builder.create(env_dir) +activate = os.path.join(env_dir, self.bindir, 'activate.csh') +test_script = os.path.join(self.env_dir, 'test_special_chars.csh') +with open(test_script, "w") as f: +f.write(f'source {shlex.quote(activate)}\n' +'python -c \'import sys; print(sys.executable)\'\n' +'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' +'deactivate\n') +out, err = check_output([csh, test_script]) +lines = out.splitlines() +self.assertTrue(env_name.encode() in lines[0]) +self.assertEndsWith(lines[1], env_name.encode()) + +# gh-124651: test quoted strings on Windows [email protected](os.name == 'nt', 'only relevant on Windows') +def test_special_chars_windows(self): +""" +Test that the template strings are quoted properly on Windows +"&q
[Python-checkins] [3.12] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) (GH-125060)
https://github.com/python/cpython/commit/bc237ed9a8d631675a4962d6627d0571dfa4c04f commit: bc237ed9a8d631675a4962d6627d0571dfa4c04f branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-08T07:24:09+01:00 summary: [3.12] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) (GH-125060) (cherry picked from commit 7ffe94fb242fd51bb07c7f0d31e94efeea3619d4) files: A Misc/NEWS.d/next/Library/2024-10-02-15-05-45.gh-issue-124653.tqsTu9.rst M Doc/library/logging.config.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 5060250ee0..bc85f4301f9ba0 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -752,16 +752,17 @@ The ``queue`` and ``listener`` keys are optional. If the ``queue`` key is present, the corresponding value can be one of the following: -* An object implementing the :class:`queue.Queue` public API. For instance, - this may be an actual instance of :class:`queue.Queue` or a subclass thereof, - or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. +* An object implementing the :meth:`Queue.put_nowait ` + and :meth:`Queue.get ` public API. For instance, this may be + an actual instance of :class:`queue.Queue` or a subclass thereof, or a proxy + obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. This is of course only possible if you are constructing or modifying the configuration dictionary in code. * A string that resolves to a callable which, when called with no arguments, returns - the :class:`queue.Queue` instance to use. That callable could be a - :class:`queue.Queue` subclass or a function which returns a suitable queue instance, + the queue instance to use. That callable could be a :class:`queue.Queue` subclass + or a function which returns a suitable queue instance, such as ``my.module.queue_factory()``. * A dict with a ``'()'`` key which is constructed in the usual way as discussed in diff --git a/Lib/logging/config.py b/Lib/logging/config.py index ac90b537d8a396..c128bc7a61e34e 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -502,7 +502,7 @@ def as_tuple(self, value): def _is_queue_like_object(obj): """Check that *obj* implements the Queue API.""" -if isinstance(obj, queue.Queue): +if isinstance(obj, (queue.Queue, queue.SimpleQueue)): return True # defer importing multiprocessing as much as possible from multiprocessing.queues import Queue as MPQueue @@ -519,13 +519,13 @@ def _is_queue_like_object(obj): # Ideally, we would have wanted to simply use strict type checking # instead of a protocol-based type checking since the latter does # not check the method signatures. -queue_interface = [ -'empty', 'full', 'get', 'get_nowait', -'put', 'put_nowait', 'join', 'qsize', -'task_done', -] +# +# Note that only 'put_nowait' and 'get' are required by the logging +# queue handler and queue listener (see gh-124653) and that other +# methods are either optional or unused. +minimal_queue_interface = ['put_nowait', 'get'] return all(callable(getattr(obj, method, None)) - for method in queue_interface) + for method in minimal_queue_interface) class DictConfigurator(BaseConfigurator): """ diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 78bcd065ad5d72..5112c2e7d60f0b 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -2391,16 +2391,22 @@ def __getattr__(self, attribute): return getattr(queue, attribute) class CustomQueueFakeProtocol(CustomQueueProtocol): -# An object implementing the Queue API (incorrect signatures). +# An object implementing the minimial Queue API for +# the logging module but with incorrect signatures. +# # The object will be considered a valid queue class since we # do not check the signatures (only callability of methods) # but will NOT be usable in production since a TypeError will -# be raised due to a missing argument. -def empty(self, x): +# be raised due to the extra argument in 'put_nowait'. +def put_nowait(self): pass class CustomQueueWrongProtocol(CustomQueueProtocol): -empty = None +put_nowait = None + +class MinimalQueueProtocol: +def put_nowait(self, x): pass +def get(self): pass def queueMaker(): return queue.Queue() @@ -3914,56 +3920,70 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'&q
[Python-checkins] [3.13] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) (GH-125059)
https://github.com/python/cpython/commit/1e820e63e7a48e8516e9c5d5d4e2dfb6cc9df552 commit: 1e820e63e7a48e8516e9c5d5d4e2dfb6cc9df552 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-08T07:23:40+01:00 summary: [3.13] gh-124653: Relax (again) detection of queue API for logging handlers (GH-124897) (GH-125059) (cherry picked from commit 7ffe94fb242fd51bb07c7f0d31e94efeea3619d4) files: A Misc/NEWS.d/next/Library/2024-10-02-15-05-45.gh-issue-124653.tqsTu9.rst M Doc/library/logging.config.rst M Lib/logging/config.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.config.rst b/Doc/library/logging.config.rst index 317ca8728248c8..0e9dc33ae2123a 100644 --- a/Doc/library/logging.config.rst +++ b/Doc/library/logging.config.rst @@ -753,16 +753,17 @@ The ``queue`` and ``listener`` keys are optional. If the ``queue`` key is present, the corresponding value can be one of the following: -* An object implementing the :class:`queue.Queue` public API. For instance, - this may be an actual instance of :class:`queue.Queue` or a subclass thereof, - or a proxy obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. +* An object implementing the :meth:`Queue.put_nowait ` + and :meth:`Queue.get ` public API. For instance, this may be + an actual instance of :class:`queue.Queue` or a subclass thereof, or a proxy + obtained by :meth:`multiprocessing.managers.SyncManager.Queue`. This is of course only possible if you are constructing or modifying the configuration dictionary in code. * A string that resolves to a callable which, when called with no arguments, returns - the :class:`queue.Queue` instance to use. That callable could be a - :class:`queue.Queue` subclass or a function which returns a suitable queue instance, + the queue instance to use. That callable could be a :class:`queue.Queue` subclass + or a function which returns a suitable queue instance, such as ``my.module.queue_factory()``. * A dict with a ``'()'`` key which is constructed in the usual way as discussed in diff --git a/Lib/logging/config.py b/Lib/logging/config.py index 735bffeaa09884..190b4f922590ca 100644 --- a/Lib/logging/config.py +++ b/Lib/logging/config.py @@ -499,7 +499,7 @@ def as_tuple(self, value): def _is_queue_like_object(obj): """Check that *obj* implements the Queue API.""" -if isinstance(obj, queue.Queue): +if isinstance(obj, (queue.Queue, queue.SimpleQueue)): return True # defer importing multiprocessing as much as possible from multiprocessing.queues import Queue as MPQueue @@ -516,13 +516,13 @@ def _is_queue_like_object(obj): # Ideally, we would have wanted to simply use strict type checking # instead of a protocol-based type checking since the latter does # not check the method signatures. -queue_interface = [ -'empty', 'full', 'get', 'get_nowait', -'put', 'put_nowait', 'join', 'qsize', -'task_done', -] +# +# Note that only 'put_nowait' and 'get' are required by the logging +# queue handler and queue listener (see gh-124653) and that other +# methods are either optional or unused. +minimal_queue_interface = ['put_nowait', 'get'] return all(callable(getattr(obj, method, None)) - for method in queue_interface) + for method in minimal_queue_interface) class DictConfigurator(BaseConfigurator): """ diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index ec6854b4bb18ed..678c23dad67faa 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -2376,16 +2376,22 @@ def __getattr__(self, attribute): return getattr(queue, attribute) class CustomQueueFakeProtocol(CustomQueueProtocol): -# An object implementing the Queue API (incorrect signatures). +# An object implementing the minimial Queue API for +# the logging module but with incorrect signatures. +# # The object will be considered a valid queue class since we # do not check the signatures (only callability of methods) # but will NOT be usable in production since a TypeError will -# be raised due to a missing argument. -def empty(self, x): +# be raised due to the extra argument in 'put_nowait'. +def put_nowait(self): pass class CustomQueueWrongProtocol(CustomQueueProtocol): -empty = None +put_nowait = None + +class MinimalQueueProtocol: +def put_nowait(self, x): pass +def get(self): pass def queueMaker(): return queue.Queue() @@ -3945,56 +3951,70 @@ def test_config_queue_handler(self): msg = str(ctx.exception) self.assertEqual(msg, "Unable to configure handler 'ah'&q
[Python-checkins] [3.13] gh-124651: Quote template strings in `venv` activation scripts (GH-124712) (GH-125813)
https://github.com/python/cpython/commit/e52095a0c1005a87eed2276af7a1f2f66e2b6483 commit: e52095a0c1005a87eed2276af7a1f2f66e2b6483 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2024-10-22T16:49:20+01:00 summary: [3.13] gh-124651: Quote template strings in `venv` activation scripts (GH-124712) (GH-125813) (cherry picked from commit d48cc82ed25e26b02eb97c6263d95dcaa1e9111b) files: A Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst M Lib/test/test_venv.py M Lib/venv/__init__.py M Lib/venv/scripts/common/activate M Lib/venv/scripts/common/activate.fish M Lib/venv/scripts/nt/activate.bat M Lib/venv/scripts/posix/activate.csh diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py index 1ef08da326c18c..6b2127bd31e40a 100644 --- a/Lib/test/test_venv.py +++ b/Lib/test/test_venv.py @@ -17,6 +17,7 @@ import sys import sysconfig import tempfile +import shlex from test.support import (captured_stdout, captured_stderr, skip_if_broken_multiprocessing_synchronize, verbose, requires_subprocess, is_android, is_apple_mobile, @@ -110,6 +111,10 @@ def get_text_file_contents(self, *args, encoding='utf-8'): result = f.read() return result +def assertEndsWith(self, string, tail): +if not string.endswith(tail): +self.fail(f"String {string!r} does not end with {tail!r}") + class BasicTest(BaseTest): """Test venv module functionality.""" @@ -488,6 +493,82 @@ def test_executable_symlinks(self): 'import sys; print(sys.executable)']) self.assertEqual(out.strip(), envpy.encode()) +# gh-124651: test quoted strings [email protected](os.name == 'nt', 'contains invalid characters on Windows') +def test_special_chars_bash(self): +""" +Test that the template strings are quoted properly (bash) +""" +rmtree(self.env_dir) +bash = shutil.which('bash') +if bash is None: +self.skipTest('bash required for this test') +env_name = '"\';&&$e|\'"' +env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) +builder = venv.EnvBuilder(clear=True) +builder.create(env_dir) +activate = os.path.join(env_dir, self.bindir, 'activate') +test_script = os.path.join(self.env_dir, 'test_special_chars.sh') +with open(test_script, "w") as f: +f.write(f'source {shlex.quote(activate)}\n' +'python -c \'import sys; print(sys.executable)\'\n' +'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' +'deactivate\n') +out, err = check_output([bash, test_script]) +lines = out.splitlines() +self.assertTrue(env_name.encode() in lines[0]) +self.assertEndsWith(lines[1], env_name.encode()) + +# gh-124651: test quoted strings [email protected](os.name == 'nt', 'contains invalid characters on Windows') +def test_special_chars_csh(self): +""" +Test that the template strings are quoted properly (csh) +""" +rmtree(self.env_dir) +csh = shutil.which('tcsh') or shutil.which('csh') +if csh is None: +self.skipTest('csh required for this test') +env_name = '"\';&&$e|\'"' +env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) +builder = venv.EnvBuilder(clear=True) +builder.create(env_dir) +activate = os.path.join(env_dir, self.bindir, 'activate.csh') +test_script = os.path.join(self.env_dir, 'test_special_chars.csh') +with open(test_script, "w") as f: +f.write(f'source {shlex.quote(activate)}\n' +'python -c \'import sys; print(sys.executable)\'\n' +'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' +'deactivate\n') +out, err = check_output([csh, test_script]) +lines = out.splitlines() +self.assertTrue(env_name.encode() in lines[0]) +self.assertEndsWith(lines[1], env_name.encode()) + +# gh-124651: test quoted strings on Windows [email protected](os.name == 'nt', 'only relevant on Windows') +def test_special_chars_windows(self): +""" +Test that the template strings are quoted properly on Windows +"&q
[Python-checkins] [3.12] gh-124651: Quote template strings in `venv` activation scripts (GH-124712) (GH-126185)
https://github.com/python/cpython/commit/8450b2482586857d689b6658f08de9c8179af7db
commit: 8450b2482586857d689b6658f08de9c8179af7db
branch: 3.12
author: Victor Stinner
committer: vsajip
date: 2024-10-31T18:09:20Z
summary:
[3.12] gh-124651: Quote template strings in `venv` activation scripts
(GH-124712) (GH-126185)
(cherry picked from commit d48cc82ed25e26b02eb97c6263d95dcaa1e9111b)
files:
A Misc/NEWS.d/next/Library/2024-09-28-02-03-04.gh-issue-124651.bLBGtH.rst
M Lib/test/test_venv.py
M Lib/venv/__init__.py
M Lib/venv/scripts/common/activate
M Lib/venv/scripts/nt/activate.bat
M Lib/venv/scripts/posix/activate.csh
M Lib/venv/scripts/posix/activate.fish
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 83d03aa3bf0d9c..8254a701e09a10 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -17,6 +17,7 @@
import sys
import sysconfig
import tempfile
+import shlex
from test.support import (captured_stdout, captured_stderr,
skip_if_broken_multiprocessing_synchronize, verbose,
requires_subprocess, is_emscripten, is_wasi,
@@ -97,6 +98,10 @@ def get_text_file_contents(self, *args, encoding='utf-8'):
result = f.read()
return result
+def assertEndsWith(self, string, tail):
+if not string.endswith(tail):
+self.fail(f"String {string!r} does not end with {tail!r}")
+
class BasicTest(BaseTest):
"""Test venv module functionality."""
@@ -446,6 +451,82 @@ def test_executable_symlinks(self):
'import sys; print(sys.executable)'])
self.assertEqual(out.strip(), envpy.encode())
+# gh-124651: test quoted strings
[email protected](os.name == 'nt', 'contains invalid characters on Windows')
+def test_special_chars_bash(self):
+"""
+Test that the template strings are quoted properly (bash)
+"""
+rmtree(self.env_dir)
+bash = shutil.which('bash')
+if bash is None:
+self.skipTest('bash required for this test')
+env_name = '"\';&&$e|\'"'
+env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
+builder = venv.EnvBuilder(clear=True)
+builder.create(env_dir)
+activate = os.path.join(env_dir, self.bindir, 'activate')
+test_script = os.path.join(self.env_dir, 'test_special_chars.sh')
+with open(test_script, "w") as f:
+f.write(f'source {shlex.quote(activate)}\n'
+'python -c \'import sys; print(sys.executable)\'\n'
+'python -c \'import os;
print(os.environ["VIRTUAL_ENV"])\'\n'
+'deactivate\n')
+out, err = check_output([bash, test_script])
+lines = out.splitlines()
+self.assertTrue(env_name.encode() in lines[0])
+self.assertEndsWith(lines[1], env_name.encode())
+
+# gh-124651: test quoted strings
[email protected](os.name == 'nt', 'contains invalid characters on Windows')
+def test_special_chars_csh(self):
+"""
+Test that the template strings are quoted properly (csh)
+"""
+rmtree(self.env_dir)
+csh = shutil.which('tcsh') or shutil.which('csh')
+if csh is None:
+self.skipTest('csh required for this test')
+env_name = '"\';&&$e|\'"'
+env_dir = os.path.join(os.path.realpath(self.env_dir), env_name)
+builder = venv.EnvBuilder(clear=True)
+builder.create(env_dir)
+activate = os.path.join(env_dir, self.bindir, 'activate.csh')
+test_script = os.path.join(self.env_dir, 'test_special_chars.csh')
+with open(test_script, "w") as f:
+f.write(f'source {shlex.quote(activate)}\n'
+'python -c \'import sys; print(sys.executable)\'\n'
+'python -c \'import os;
print(os.environ["VIRTUAL_ENV"])\'\n'
+'deactivate\n')
+out, err = check_output([csh, test_script])
+lines = out.splitlines()
+self.assertTrue(env_name.encode() in lines[0])
+self.assertEndsWith(lines[1], env_name.encode())
+
+# gh-124651: test quoted strings on Windows
[email protected](os.name == 'nt', 'only relevant on Windows')
+def test_special_chars_windows(self):
+"""
+Test that the template strings are quoted properly on Windows
+"""
+rmtree(self.env_dir)
+env_name = "'&a
[Python-checkins] gh-129143: Fix incorrect documentation for logging.Handler.close(). (GH-129950)
https://github.com/python/cpython/commit/7c156a63d3d5aadff0d40af73c0f622f7a0fcea5 commit: 7c156a63d3d5aadff0d40af73c0f622f7a0fcea5 branch: main author: Vinay Sajip committer: vsajip date: 2025-02-10T11:13:52Z summary: gh-129143: Fix incorrect documentation for logging.Handler.close(). (GH-129950) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 19ae024f9eeffa..34bb46f0bb1ecb 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -592,10 +592,12 @@ subclasses. However, the :meth:`!__init__` method in subclasses needs to call .. method:: Handler.close() - Tidy up any resources used by the handler. This version does no output but - removes the handler from an internal list of handlers which is closed when - :func:`shutdown` is called. Subclasses should ensure that this gets called - from overridden :meth:`close` methods. + Tidy up any resources used by the handler. This version does no output + but removes the handler from an internal map of handlers, which is used + for handler lookup by name. + + Subclasses should ensure that this gets called from overridden :meth:`close` + methods. .. method:: Handler.handle(record) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` (GH-127726) (GH-129956)
https://github.com/python/cpython/commit/2db4a9d0535af198452136312f9f0b232fa04cf0 commit: 2db4a9d0535af198452136312f9f0b232fa04cf0 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-02-10T14:29:57Z summary: [3.12] gh-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` (GH-127726) (GH-129956) (cherry picked from commit d7672e5d5a7b9580a72dbe75d3a9e8840bcc604c) files: A Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst M Lib/logging/handlers.py diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 73757758af2a2e..0aa7b15ee73be7 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1031,7 +1031,8 @@ def __init__(self, mailhost, fromaddr, toaddrs, subject, only be used when authentication credentials are supplied. The tuple will be either an empty tuple, or a single-value tuple with the name of a keyfile, or a 2-value tuple with the names of the keyfile and -certificate file. (This tuple is passed to the `starttls` method). +certificate file. (This tuple is passed to the +`ssl.SSLContext.load_cert_chain` method). A timeout in seconds can be specified for the SMTP connection (the default is one second). """ @@ -1084,8 +1085,23 @@ def emit(self, record): msg.set_content(self.format(record)) if self.username: if self.secure is not None: +import ssl + +try: +keyfile = self.secure[0] +except IndexError: +keyfile = None + +try: +certfile = self.secure[1] +except IndexError: +certfile = None + +context = ssl._create_stdlib_context( +certfile=certfile, keyfile=keyfile +) smtp.ehlo() -smtp.starttls(*self.secure) +smtp.starttls(context=context) smtp.ehlo() smtp.login(self.username, self.password) smtp.send_message(msg) diff --git a/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst b/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst new file mode 100644 index 00..40450cddc956c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst @@ -0,0 +1 @@ +Fix handling of the ``secure`` argument of :class:`logging.handlers.SMTPHandler`. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` (GH-127726) (GH-129955)
https://github.com/python/cpython/commit/fd665d5f09e98f8988e46b16b2596017ed7525a2 commit: fd665d5f09e98f8988e46b16b2596017ed7525a2 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-02-10T14:30:31Z summary: [3.13] gh-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` (GH-127726) (GH-129955) (cherry picked from commit d7672e5d5a7b9580a72dbe75d3a9e8840bcc604c) files: A Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst M Lib/logging/handlers.py diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 1cba64fd554100..da9c59c119f3db 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1040,7 +1040,8 @@ def __init__(self, mailhost, fromaddr, toaddrs, subject, only be used when authentication credentials are supplied. The tuple will be either an empty tuple, or a single-value tuple with the name of a keyfile, or a 2-value tuple with the names of the keyfile and -certificate file. (This tuple is passed to the `starttls` method). +certificate file. (This tuple is passed to the +`ssl.SSLContext.load_cert_chain` method). A timeout in seconds can be specified for the SMTP connection (the default is one second). """ @@ -1093,8 +1094,23 @@ def emit(self, record): msg.set_content(self.format(record)) if self.username: if self.secure is not None: +import ssl + +try: +keyfile = self.secure[0] +except IndexError: +keyfile = None + +try: +certfile = self.secure[1] +except IndexError: +certfile = None + +context = ssl._create_stdlib_context( +certfile=certfile, keyfile=keyfile +) smtp.ehlo() -smtp.starttls(*self.secure) +smtp.starttls(context=context) smtp.ehlo() smtp.login(self.username, self.password) smtp.send_message(msg) diff --git a/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst b/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst new file mode 100644 index 00..40450cddc956c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst @@ -0,0 +1 @@ +Fix handling of the ``secure`` argument of :class:`logging.handlers.SMTPHandler`. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` (GH-127726)
https://github.com/python/cpython/commit/d7672e5d5a7b9580a72dbe75d3a9e8840bcc604c commit: d7672e5d5a7b9580a72dbe75d3a9e8840bcc604c branch: main author: s-hamann <[email protected]> committer: vsajip date: 2025-02-10T12:34:27Z summary: gh-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` (GH-127726) GH-127712: Fix `secure` argument of `logging.handlers.SMTPHandler` Python 3.12 removed support for the `keyfile` and `certfile` parameters in `smtplib.SMTP.starttls()`, requiring a `ssl.SSLContext` instead. `SMTPHandler` now creates a context from the `secure` tuple and passes that to `starttls`. files: A Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst M Lib/logging/handlers.py diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 017c9ab409b7bc..9abadbf5cdd1df 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1043,7 +1043,8 @@ def __init__(self, mailhost, fromaddr, toaddrs, subject, only be used when authentication credentials are supplied. The tuple will be either an empty tuple, or a single-value tuple with the name of a keyfile, or a 2-value tuple with the names of the keyfile and -certificate file. (This tuple is passed to the `starttls` method). +certificate file. (This tuple is passed to the +`ssl.SSLContext.load_cert_chain` method). A timeout in seconds can be specified for the SMTP connection (the default is one second). """ @@ -1096,8 +1097,23 @@ def emit(self, record): msg.set_content(self.format(record)) if self.username: if self.secure is not None: +import ssl + +try: +keyfile = self.secure[0] +except IndexError: +keyfile = None + +try: +certfile = self.secure[1] +except IndexError: +certfile = None + +context = ssl._create_stdlib_context( +certfile=certfile, keyfile=keyfile +) smtp.ehlo() -smtp.starttls(*self.secure) +smtp.starttls(context=context) smtp.ehlo() smtp.login(self.username, self.password) smtp.send_message(msg) diff --git a/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst b/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst new file mode 100644 index 00..40450cddc956c1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-12-07-20-33-43.gh-issue-127712.Uzsij4.rst @@ -0,0 +1 @@ +Fix handling of the ``secure`` argument of :class:`logging.handlers.SMTPHandler`. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-129143: Fix incorrect documentation for logging.Handler.close(). (GH-129950) (GH-129952)
https://github.com/python/cpython/commit/08a9b7c385bbdc36b816fe1d1b29ff87e10082d7 commit: 08a9b7c385bbdc36b816fe1d1b29ff87e10082d7 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-02-10T11:26:18Z summary: [3.12] gh-129143: Fix incorrect documentation for logging.Handler.close(). (GH-129950) (GH-129952) (cherry picked from commit 7c156a63d3d5aadff0d40af73c0f622f7a0fcea5) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 22f135bbecd3a3..5795645f71d1cd 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -592,10 +592,12 @@ subclasses. However, the :meth:`!__init__` method in subclasses needs to call .. method:: Handler.close() - Tidy up any resources used by the handler. This version does no output but - removes the handler from an internal list of handlers which is closed when - :func:`shutdown` is called. Subclasses should ensure that this gets called - from overridden :meth:`close` methods. + Tidy up any resources used by the handler. This version does no output + but removes the handler from an internal map of handlers, which is used + for handler lookup by name. + + Subclasses should ensure that this gets called from overridden :meth:`close` + methods. .. method:: Handler.handle(record) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-129143: Fix incorrect documentation for logging.Handler.close(). (GH-129950) (GH-129951)
https://github.com/python/cpython/commit/a00f5a84fdefac2acda2a1a636b07b3fada08f97 commit: a00f5a84fdefac2acda2a1a636b07b3fada08f97 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-02-10T11:26:50Z summary: [3.13] gh-129143: Fix incorrect documentation for logging.Handler.close(). (GH-129950) (GH-129951) (cherry picked from commit 7c156a63d3d5aadff0d40af73c0f622f7a0fcea5) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index e930834209692e..3d84cc70684de3 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -592,10 +592,12 @@ subclasses. However, the :meth:`!__init__` method in subclasses needs to call .. method:: Handler.close() - Tidy up any resources used by the handler. This version does no output but - removes the handler from an internal list of handlers which is closed when - :func:`shutdown` is called. Subclasses should ensure that this gets called - from overridden :meth:`close` methods. + Tidy up any resources used by the handler. This version does no output + but removes the handler from an internal map of handlers, which is used + for handler lookup by name. + + Subclasses should ensure that this gets called from overridden :meth:`close` + methods. .. method:: Handler.handle(record) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] gh-126400: Add TCP socket timeout to SysLogHandler to prevent blocking (GH-126716)
https://github.com/python/cpython/commit/fdcedfd3cf1a645634dee354ee349d966c56348f commit: fdcedfd3cf1a645634dee354ee349d966c56348f branch: main author: Hod <[email protected]> committer: vsajip date: 2025-01-29T19:37:43Z summary: gh-126400: Add TCP socket timeout to SysLogHandler to prevent blocking (GH-126716) Co-authored-by: Vinay Sajip files: A Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst M Doc/library/logging.handlers.rst M Lib/logging/handlers.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index 5a081f9e7add99..ffb54591b3563b 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -613,7 +613,7 @@ The :class:`SysLogHandler` class, located in the :mod:`logging.handlers` module, supports sending logging messages to a remote or local Unix syslog. -.. class:: SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM) +.. class:: SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM, timeout=None) Returns a new instance of the :class:`SysLogHandler` class intended to communicate with a remote Unix machine whose address is given by *address* in @@ -626,6 +626,11 @@ supports sending logging messages to a remote or local Unix syslog. *socktype* argument, which defaults to :const:`socket.SOCK_DGRAM` and thus opens a UDP socket. To open a TCP socket (for use with the newer syslog daemons such as rsyslog), specify a value of :const:`socket.SOCK_STREAM`. + If *timeout* is specified, it sets a timeout (in seconds) for the socket operations. + This can help prevent the program from hanging indefinitely if the syslog server is + unreachable. By default, *timeout* is ``None``, meaning no timeout is applied. + + Note that if your server is not listening on UDP port 514, :class:`SysLogHandler` may appear not to work. In that case, check what @@ -645,6 +650,8 @@ supports sending logging messages to a remote or local Unix syslog. .. versionchanged:: 3.2 *socktype* was added. + .. versionchanged:: 3.14 + *timeout* was added. .. method:: close() diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 1cba64fd554100..017c9ab409b7bc 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -855,7 +855,7 @@ class SysLogHandler(logging.Handler): } def __init__(self, address=('localhost', SYSLOG_UDP_PORT), - facility=LOG_USER, socktype=None): + facility=LOG_USER, socktype=None, timeout=None): """ Initialize a handler. @@ -872,6 +872,7 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT), self.address = address self.facility = facility self.socktype = socktype +self.timeout = timeout self.socket = None self.createSocket() @@ -933,6 +934,8 @@ def createSocket(self): err = sock = None try: sock = socket.socket(af, socktype, proto) +if self.timeout: +sock.settimeout(self.timeout) if socktype == socket.SOCK_STREAM: sock.connect(sa) break diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 2e5f6475ae3b1e..e34fe45fd68e52 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -22,6 +22,7 @@ import logging.handlers import logging.config + import codecs import configparser import copy @@ -2095,6 +2096,18 @@ def test_udp_reconnection(self): self.handled.wait(support.LONG_TIMEOUT) self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00') +@patch('socket.socket') +def test_tcp_timeout(self, mock_socket): +instance_mock_sock = mock_socket.return_value +instance_mock_sock.connect.side_effect = socket.timeout + +with self.assertRaises(socket.timeout): +logging.handlers.SysLogHandler(address=('localhost', 514), + socktype=socket.SOCK_STREAM, + timeout=1) + +instance_mock_sock.close.assert_called() + @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required") class UnixSysLogHandlerTest(SysLogHandlerTest): diff --git a/Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst b/Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst new file mode 100644 index 00..1532faf4b7d6f5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst @@ -0,0 +1,2 @@ +Add a socket *timeout* keyword argument to +:class:`log
[Python-checkins] [3.13] gh-132106: Ensure that running `logging.handlers.QueueListener… (GH-132471)
https://github.com/python/cpython/commit/1dcdac6e084471c14b4690ae9154b928e93a7222
commit: 1dcdac6e084471c14b4690ae9154b928e93a7222
branch: 3.13
author: Vinay Sajip
committer: vsajip
date: 2025-04-13T13:00:50+01:00
summary:
[3.13] gh-132106: Ensure that running `logging.handlers.QueueListener…
(GH-132471)
Cherry-picked using 5863cd70b8782313b52bb8c71a4127d7ea4c50e9
files:
A Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst
M Doc/library/logging.handlers.rst
M Lib/logging/handlers.py
M Lib/test/test_logging.py
diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst
index 5a081f9e7add99..e63c253e123552 100644
--- a/Doc/library/logging.handlers.rst
+++ b/Doc/library/logging.handlers.rst
@@ -1172,6 +1172,10 @@ possible, while any potentially slow operations (such as
sending an email via
This starts up a background thread to monitor the queue for
LogRecords to process.
+ .. versionchanged:: next
+ Raises :exc:`RuntimeError` if called and the listener is already
+ running.
+
.. method:: stop()
Stops the listener.
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index da9c59c119f3db..d3ea06c731ef89 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -1545,6 +1545,9 @@ def start(self):
This starts up a background thread to monitor the queue for
LogRecords to process.
"""
+if self._thread is not None:
+raise RuntimeError("Listener already started")
+
self._thread = t = threading.Thread(target=self._monitor)
t.daemon = True
t.start()
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 678c23dad67faa..58e0381c4aa934 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -4300,8 +4300,6 @@ def test_formatting(self):
self.assertEqual(formatted_msg, log_record.msg)
self.assertEqual(formatted_msg, log_record.message)
[email protected](hasattr(logging.handlers, 'QueueListener'),
- 'logging.handlers.QueueListener required for this
test')
def test_queue_listener(self):
handler = TestHandler(support.Matcher())
listener = logging.handlers.QueueListener(self.queue, handler)
@@ -4336,8 +4334,18 @@ def test_queue_listener(self):
self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
handler.close()
[email protected](hasattr(logging.handlers, 'QueueListener'),
- 'logging.handlers.QueueListener required for this
test')
+# doesn't hurt to call stop() more than once.
+listener.stop()
+self.assertIsNone(listener._thread)
+
+def test_queue_listener_multi_start(self):
+handler = TestHandler(support.Matcher())
+listener = logging.handlers.QueueListener(self.queue, handler)
+listener.start()
+self.assertRaises(RuntimeError, listener.start)
+listener.stop()
+self.assertIsNone(listener._thread)
+
def test_queue_listener_with_StreamHandler(self):
# Test that traceback and stack-info only appends once (bpo-34334,
bpo-46755).
listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
@@ -4352,8 +4360,6 @@ def test_queue_listener_with_StreamHandler(self):
self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
self.assertEqual(self.stream.getvalue().strip().count('Stack'), 1)
[email protected](hasattr(logging.handlers, 'QueueListener'),
- 'logging.handlers.QueueListener required for this
test')
def test_queue_listener_with_multiple_handlers(self):
# Test that queue handler format doesn't affect other handler formats
(bpo-35726).
self.que_hdlr.setFormatter(self.root_formatter)
diff --git
a/Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst
b/Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst
new file mode 100644
index 00..b6d58a29f9b42f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst
@@ -0,0 +1,2 @@
+:meth:`QueueListener.start ` now
+raises a :exc:`RuntimeError` if the listener is already started.
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] gh-115032: Deprecate support for custom logging handlers with 'strm' argument. (GH-115314)
https://github.com/python/cpython/commit/28a2fd031e3e9c28bf32670c86b60ea74d0c470d
commit: 28a2fd031e3e9c28bf32670c86b60ea74d0c470d
branch: main
author: Mariusz Felisiak
committer: vsajip
date: 2025-04-27T22:18:14+01:00
summary:
gh-115032: Deprecate support for custom logging handlers with 'strm' argument.
(GH-115314)
files:
A Misc/NEWS.d/next/Library/2025-04-26-12-25-42.gh-issue-115032.jnM2Co.rst
M Doc/whatsnew/3.14.rst
M Lib/logging/config.py
M Lib/test/test_logging.py
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index e9abb41cfd5251..13ac8d63aef33c 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1580,6 +1580,11 @@ Deprecated
or *sequence* as keyword arguments is now deprecated.
(Contributed by Kirill Podoprigora in :gh:`121676`.)
+* :mod:`logging`:
+ Support for custom logging handlers with the *strm* argument is deprecated
+ and scheduled for removal in Python 3.16. Define handlers with the *stream*
+ argument instead. (Contributed by Mariusz Felisiak in :gh:`115032`.)
+
* :mod:`!nturl2path`: This module is now deprecated. Call
:func:`urllib.request.url2pathname` and :func:`~urllib.request.pathname2url`
instead.
diff --git a/Lib/logging/config.py b/Lib/logging/config.py
index 6a6a7f726f7e0c..c994349fd6eee5 100644
--- a/Lib/logging/config.py
+++ b/Lib/logging/config.py
@@ -865,6 +865,8 @@ def configure_handler(self, config):
else:
factory = klass
kwargs = {k: config[k] for k in config if (k != '.' and
valid_ident(k))}
+# When deprecation ends for using the 'strm' parameter, remove the
+# "except TypeError ..."
try:
result = factory(**kwargs)
except TypeError as te:
@@ -876,6 +878,15 @@ def configure_handler(self, config):
#(e.g. by Django)
kwargs['strm'] = kwargs.pop('stream')
result = factory(**kwargs)
+
+import warnings
+warnings.warn(
+"Support for custom logging handlers with the 'strm' argument "
+"is deprecated and scheduled for removal in Python 3.16. "
+"Define handlers with the 'stream' argument instead.",
+DeprecationWarning,
+stacklevel=2,
+)
if formatter:
result.setFormatter(formatter)
if level is not None:
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 9305844829a500..de9108288a72f5 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -3281,6 +3281,37 @@ def format(self, record):
}
}
+# Remove when deprecation ends.
+class DeprecatedStrmHandler(logging.StreamHandler):
+def __init__(self, strm=None):
+super().__init__(stream=strm)
+
+config_custom_handler_with_deprecated_strm_arg = {
+"version": 1,
+"formatters": {
+"form1": {
+"format": "%(levelname)s ++ %(message)s",
+},
+},
+"handlers": {
+"hand1": {
+"class": DeprecatedStrmHandler,
+"formatter": "form1",
+"level": "NOTSET",
+"stream": "ext://sys.stdout",
+},
+},
+"loggers": {
+"compiler.parser": {
+"level": "DEBUG",
+"handlers": ["hand1"],
+},
+},
+"root": {
+"level": "WARNING",
+},
+}
+
def apply_config(self, conf):
logging.config.dictConfig(conf)
@@ -3370,6 +3401,15 @@ def test_config5_ok(self):
self.test_config1_ok(config=self.config5)
self.check_handler('hand1', CustomHandler)
+def test_deprecation_warning_custom_handler_with_strm_arg(self):
+msg = (
+"Support for custom logging handlers with the 'strm' argument "
+"is deprecated and scheduled for removal in Python 3.16. "
+"Define handlers with the 'stream' argument instead."
+)
+with self.assertWarnsRegex(DeprecationWarning, msg):
+
self.test_config1_ok(config=self.config_custom_handler_with_deprecated_strm_arg)
+
def test_config6_failure(self):
self.assertRaises(Exception, self.apply_config, self.config6)
diff --git
a/Misc/NEWS.d/next/Library/2025-04-26-12-25-42.gh-issue-115032.jnM2Co.rst
b/Misc/NEWS.d/next/Library/2025-04-26-12-25-42.gh-issue-115032.jnM2Co.rst
new file mode 100644
index 00..80c10aef06996d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-04-26-12-25-
[Python-checkins] gh-132106: Ensure that running `logging.handlers.QueueListener` cannot be started again (GH-132444)
https://github.com/python/cpython/commit/5863cd70b8782313b52bb8c71a4127d7ea4c50e9 commit: 5863cd70b8782313b52bb8c71a4127d7ea4c50e9 branch: main author: Charles Machalow committer: vsajip date: 2025-04-13T08:53:13+01:00 summary: gh-132106: Ensure that running `logging.handlers.QueueListener` cannot be started again (GH-132444) Prevents a thread leak Co-authored-by: Bénédikt Tran <[email protected]> files: A Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst M Doc/library/logging.handlers.rst M Doc/whatsnew/3.14.rst M Lib/logging/handlers.py M Lib/test/test_logging.py diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst index b737fe311dfb6e..72312b512a5884 100644 --- a/Doc/library/logging.handlers.rst +++ b/Doc/library/logging.handlers.rst @@ -1186,6 +1186,10 @@ possible, while any potentially slow operations (such as sending an email via This starts up a background thread to monitor the queue for LogRecords to process. + .. versionchanged:: next + Raises :exc:`RuntimeError` if called and the listener is already + running. + .. method:: stop() Stops the listener. diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 762d53eeb2df1a..421d12660b7956 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -819,6 +819,10 @@ logging.handlers manager protocol, allowing it to be used in a :keyword:`with` statement. (Contributed by Charles Machalow in :gh:`132106`.) +* :meth:`QueueListener.start ` now + raises a :exc:`RuntimeError` if the listener is already started. + (Contributed by Charles Machalow in :gh:`132106`.) + mimetypes - diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 0571ed2356345a..2748b5941eade2 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1561,6 +1561,9 @@ def start(self): This starts up a background thread to monitor the queue for LogRecords to process. """ +if self._thread is not None: +raise RuntimeError("Listener already started") + self._thread = t = threading.Thread(target=self._monitor) t.daemon = True t.start() diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 11f6b64abe28fb..9305844829a500 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4356,6 +4356,17 @@ def test_queue_listener_context_manager(self): listener.stop() self.assertIsNone(listener._thread) +def test_queue_listener_multi_start(self): +handler = TestHandler(support.Matcher()) +with logging.handlers.QueueListener(self.queue, handler) as listener: +self.assertRaises(RuntimeError, listener.start) + +with listener: +self.assertRaises(RuntimeError, listener.start) + +listener.start() +listener.stop() + def test_queue_listener_with_StreamHandler(self): # Test that traceback and stack-info only appends once (bpo-34334, bpo-46755). listener = logging.handlers.QueueListener(self.queue, self.root_hdlr) diff --git a/Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst b/Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst new file mode 100644 index 00..b6d58a29f9b42f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-12-09-30-24.gh-issue-132106.OxUds3.rst @@ -0,0 +1,2 @@ +:meth:`QueueListener.start ` now +raises a :exc:`RuntimeError` if the listener is already started. ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] gh-112507: Detect Cygwin and MSYS with `uname` instead of `$OSTYPE` (GH-112508) (GH-130674)
https://github.com/python/cpython/commit/4eb14211a58bda35134caa40b1d36ccb0f16383b
commit: 4eb14211a58bda35134caa40b1d36ccb0f16383b
branch: 3.12
author: Vinay Sajip
committer: vsajip
date: 2025-02-28T09:23:45Z
summary:
[3.12] gh-112507: Detect Cygwin and MSYS with `uname` instead of `$OSTYPE`
(GH-112508) (GH-130674)
(cherry picked from commit d7b5f102319bb0389c5248e9ecf533eae4163424)
files:
M Lib/venv/scripts/common/activate
diff --git a/Lib/venv/scripts/common/activate b/Lib/venv/scripts/common/activate
index 74825877c38313..70673a265d41f8 100644
--- a/Lib/venv/scripts/common/activate
+++ b/Lib/venv/scripts/common/activate
@@ -37,19 +37,26 @@ deactivate () {
deactivate nondestructive
# on Windows, a path can contain colons and backslashes and has to be
converted:
-if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
-# transform D:\path\to\venv to /d/path/to/venv on MSYS
-# and to /cygdrive/d/path/to/venv on Cygwin
-export VIRTUAL_ENV=$(cygpath __VENV_DIR__)
-else
-# use the path as-is
-export VIRTUAL_ENV=__VENV_DIR__
-fi
+case "$(uname)" in
+CYGWIN*|MSYS*|MINGW*)
+# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW
+# and to /cygdrive/d/path/to/venv on Cygwin
+VIRTUAL_ENV=$(cygpath __VENV_DIR__)
+export VIRTUAL_ENV
+;;
+*)
+# use the path as-is
+export VIRTUAL_ENV=__VENV_DIR__
+;;
+esac
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/"__VENV_BIN_NAME__":$PATH"
export PATH
+VIRTUAL_ENV_PROMPT=__VENV_PROMPT__
+export VIRTUAL_ENV_PROMPT
+
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
@@ -60,10 +67,8 @@ fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
-PS1=__VENV_PROMPT__"${PS1:-}"
+PS1="("__VENV_PROMPT__") ${PS1:-}"
export PS1
-VIRTUAL_ENV_PROMPT=__VENV_PROMPT__
-export VIRTUAL_ENV_PROMPT
fi
# Call hash to forget past commands. Without forgetting
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] gh-130486: Fix test_venv fails from within venv (GH-130487)
https://github.com/python/cpython/commit/d780f0af2bd7b9ef8cf46d28c5d495d1c980b1f0
commit: d780f0af2bd7b9ef8cf46d28c5d495d1c980b1f0
branch: main
author: Andrii Hrimov
committer: vsajip
date: 2025-03-04T07:48:07Z
summary:
gh-130486: Fix test_venv fails from within venv (GH-130487)
files:
M Lib/test/test_venv.py
diff --git a/Lib/test/test_venv.py b/Lib/test/test_venv.py
index 6e23097deaf221..adc86a49b0668d 100644
--- a/Lib/test/test_venv.py
+++ b/Lib/test/test_venv.py
@@ -228,25 +228,27 @@ def test_upgrade_dependencies(self):
builder = venv.EnvBuilder()
bin_path = 'bin'
python_exe = os.path.split(sys.executable)[1]
+expected_exe = os.path.basename(sys._base_executable)
+
if sys.platform == 'win32':
bin_path = 'Scripts'
if
os.path.normcase(os.path.splitext(python_exe)[0]).endswith('_d'):
-python_exe = 'python_d.exe'
+expected_exe = 'python_d'
else:
-python_exe = 'python.exe'
+expected_exe = 'python'
+python_exe = expected_exe + '.exe'
+
with tempfile.TemporaryDirectory() as fake_env_dir:
expect_exe = os.path.normcase(
-os.path.join(fake_env_dir, bin_path, python_exe)
+os.path.join(fake_env_dir, bin_path, expected_exe)
)
if sys.platform == 'win32':
expect_exe = os.path.normcase(os.path.realpath(expect_exe))
def pip_cmd_checker(cmd, **kwargs):
-cmd[0] = os.path.normcase(cmd[0])
self.assertEqual(
-cmd,
+cmd[1:],
[
-expect_exe,
'-m',
'pip',
'install',
@@ -254,6 +256,9 @@ def pip_cmd_checker(cmd, **kwargs):
'pip',
]
)
+exe_dir = os.path.normcase(os.path.dirname(cmd[0]))
+expected_dir = os.path.normcase(os.path.dirname(expect_exe))
+self.assertEqual(exe_dir, expected_dir)
fake_context = builder.ensure_directories(fake_env_dir)
with patch('venv.subprocess.check_output', pip_cmd_checker):
@@ -681,7 +686,8 @@ def test_zippath_from_non_installed_posix(self):
self.addCleanup(rmtree, non_installed_dir)
bindir = os.path.join(non_installed_dir, self.bindir)
os.mkdir(bindir)
-shutil.copy2(sys.executable, bindir)
+python_exe = os.path.basename(sys.executable)
+shutil.copy2(sys.executable, os.path.join(bindir, python_exe))
libdir = os.path.join(non_installed_dir, platlibdir, self.lib[1])
os.makedirs(libdir)
landmark = os.path.join(libdir, "os.py")
@@ -717,7 +723,7 @@ def test_zippath_from_non_installed_posix(self):
else:
additional_pythonpath_for_non_installed.append(
eachpath)
-cmd = [os.path.join(non_installed_dir, self.bindir, self.exe),
+cmd = [os.path.join(non_installed_dir, self.bindir, python_exe),
"-m",
"venv",
"--without-pip",
@@ -748,7 +754,8 @@ def test_zippath_from_non_installed_posix(self):
subprocess.check_call(cmd, env=child_env)
# Now check the venv created from the non-installed python has
# correct zip path in pythonpath.
-cmd = [self.envpy(), '-S', '-c', 'import sys; print(sys.path)']
+target_python = os.path.join(self.env_dir, self.bindir, python_exe)
+cmd = [target_python, '-S', '-c', 'import sys; print(sys.path)']
out, err = check_output(cmd)
self.assertTrue(zip_landmark.encode() in out)
___
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]
[Python-checkins] gh-127805: Clarify Formatter initialization in logging.rst. (GH-127850)
https://github.com/python/cpython/commit/5d66c55c8ad0a0aeff8d06021ddca1d02c5f4416 commit: 5d66c55c8ad0a0aeff8d06021ddca1d02c5f4416 branch: main author: UV committer: vsajip date: 2025-02-21T07:10:04Z summary: gh-127805: Clarify Formatter initialization in logging.rst. (GH-127850) files: M Doc/library/logging.rst diff --git a/Doc/library/logging.rst b/Doc/library/logging.rst index 34bb46f0bb1ecb..72190e97240514 100644 --- a/Doc/library/logging.rst +++ b/Doc/library/logging.rst @@ -562,7 +562,8 @@ subclasses. However, the :meth:`!__init__` method in subclasses needs to call .. method:: Handler.setFormatter(fmt) - Sets the :class:`Formatter` for this handler to *fmt*. + Sets the formatter for this handler to *fmt*. + The *fmt* argument must be a :class:`Formatter` instance or ``None``. .. method:: Handler.addFilter(filter) ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [doc]: Update logging cookbook to mention domain socket configuration in a recipe. (GH-130348)
https://github.com/python/cpython/commit/8cbcf51d614815df3ab7ea557f04e6b4b386968e commit: 8cbcf51d614815df3ab7ea557f04e6b4b386968e branch: main author: Vinay Sajip committer: vsajip date: 2025-02-20T14:09:15Z summary: [doc]: Update logging cookbook to mention domain socket configuration in a recipe. (GH-130348) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 3cd2f1d96a7b34..f08f45179980f3 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -825,9 +825,9 @@ To test these files, do the following in a POSIX environment: which will lead to records being written to the log. #. Inspect the log files in the :file:`run` subdirectory. You should see the - most recent log lines in files matching the pattern :file:`app.log*`. They won't be in - any particular order, since they have been handled concurrently by different - worker processes in a non-deterministic way. + most recent log lines in files matching the pattern :file:`app.log*`. They + won't be in any particular order, since they have been handled concurrently + by different worker processes in a non-deterministic way. #. You can shut down the listener and the web application by running ``venv/bin/supervisorctl -c supervisor.conf shutdown``. @@ -835,6 +835,19 @@ To test these files, do the following in a POSIX environment: You may need to tweak the configuration files in the unlikely event that the configured ports clash with something else in your test environment. +The default configuration uses a TCP socket on port 9020. You can use a Unix +Domain socket instead of a TCP socket by doing the following: + +#. In :file:`listener.json`, add a ``socket`` key with the path to the domain + socket you want to use. If this key is present, the listener listens on the + corresponding domain socket and not on a TCP socket (the ``port`` key is + ignored). + +#. In :file:`webapp.json`, change the socket handler configuration dictionary + so that the ``host`` value is the path to the domain socket, and set the + ``port`` value to ``null``. + + .. currentmodule:: logging .. _context-info: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.12] [doc]: Update logging cookbook to mention domain socket configuration in a recipe. (GH-130348) (GH-130352)
https://github.com/python/cpython/commit/77cba87472a64238cb01e65280bbd7fd3fe70b60 commit: 77cba87472a64238cb01e65280bbd7fd3fe70b60 branch: 3.12 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-02-20T14:20:00Z summary: [3.12] [doc]: Update logging cookbook to mention domain socket configuration in a recipe. (GH-130348) (GH-130352) (cherry picked from commit 8cbcf51d614815df3ab7ea557f04e6b4b386968e) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index c7b463d130346e..b683540412a0ec 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -825,9 +825,9 @@ To test these files, do the following in a POSIX environment: which will lead to records being written to the log. #. Inspect the log files in the :file:`run` subdirectory. You should see the - most recent log lines in files matching the pattern :file:`app.log*`. They won't be in - any particular order, since they have been handled concurrently by different - worker processes in a non-deterministic way. + most recent log lines in files matching the pattern :file:`app.log*`. They + won't be in any particular order, since they have been handled concurrently + by different worker processes in a non-deterministic way. #. You can shut down the listener and the web application by running ``venv/bin/supervisorctl -c supervisor.conf shutdown``. @@ -835,6 +835,19 @@ To test these files, do the following in a POSIX environment: You may need to tweak the configuration files in the unlikely event that the configured ports clash with something else in your test environment. +The default configuration uses a TCP socket on port 9020. You can use a Unix +Domain socket instead of a TCP socket by doing the following: + +#. In :file:`listener.json`, add a ``socket`` key with the path to the domain + socket you want to use. If this key is present, the listener listens on the + corresponding domain socket and not on a TCP socket (the ``port`` key is + ignored). + +#. In :file:`webapp.json`, change the socket handler configuration dictionary + so that the ``host`` value is the path to the domain socket, and set the + ``port`` value to ``null``. + + .. currentmodule:: logging .. _context-info: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] [doc]: Update logging cookbook to mention domain socket configuration in a recipe. (GH-130348) (GH-130351)
https://github.com/python/cpython/commit/0a992ff38339728b7589f20ced1bb3c611ff2502 commit: 0a992ff38339728b7589f20ced1bb3c611ff2502 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-02-20T14:20:26Z summary: [3.13] [doc]: Update logging cookbook to mention domain socket configuration in a recipe. (GH-130348) (GH-130351) (cherry picked from commit 8cbcf51d614815df3ab7ea557f04e6b4b386968e) files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 849a7526d40180..17fc4619db327a 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -825,9 +825,9 @@ To test these files, do the following in a POSIX environment: which will lead to records being written to the log. #. Inspect the log files in the :file:`run` subdirectory. You should see the - most recent log lines in files matching the pattern :file:`app.log*`. They won't be in - any particular order, since they have been handled concurrently by different - worker processes in a non-deterministic way. + most recent log lines in files matching the pattern :file:`app.log*`. They + won't be in any particular order, since they have been handled concurrently + by different worker processes in a non-deterministic way. #. You can shut down the listener and the web application by running ``venv/bin/supervisorctl -c supervisor.conf shutdown``. @@ -835,6 +835,19 @@ To test these files, do the following in a POSIX environment: You may need to tweak the configuration files in the unlikely event that the configured ports clash with something else in your test environment. +The default configuration uses a TCP socket on port 9020. You can use a Unix +Domain socket instead of a TCP socket by doing the following: + +#. In :file:`listener.json`, add a ``socket`` key with the path to the domain + socket you want to use. If this key is present, the listener listens on the + corresponding domain socket and not on a TCP socket (the ``port`` key is + ignored). + +#. In :file:`webapp.json`, change the socket handler configuration dictionary + so that the ``host`` value is the path to the domain socket, and set the + ``port`` value to ``null``. + + .. currentmodule:: logging .. _context-info: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: [email protected]
[Python-checkins] [3.13] gh-91555: disable logger while handling log record (GH-131812) (GH-133898)
https://github.com/python/cpython/commit/8e923f36596370aedfdfb12251447bface41317a commit: 8e923f36596370aedfdfb12251447bface41317a branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-05-12T07:29:06+01:00 summary: [3.13] gh-91555: disable logger while handling log record (GH-131812) (GH-133898) Co-authored-by: Duane Griffin files: A Misc/NEWS.d/next/Library/2025-03-30-16-42-38.gh-issue-91555.ShVtwW.rst M Lib/logging/__init__.py M Lib/test/test_logging.py diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py index aa9b79d8cab4bb..283a1055182b63 100644 --- a/Lib/logging/__init__.py +++ b/Lib/logging/__init__.py @@ -1474,6 +1474,8 @@ class Logger(Filterer): level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. There is no arbitrary limit to the depth of nesting. """ +_tls = threading.local() + def __init__(self, name, level=NOTSET): """ Initialize the logger with a name and an optional level. @@ -1670,14 +1672,19 @@ def handle(self, record): This method is used for unpickled records received from a socket, as well as those created locally. Logger-level filtering is applied. """ -if self.disabled: -return -maybe_record = self.filter(record) -if not maybe_record: +if self._is_disabled(): return -if isinstance(maybe_record, LogRecord): -record = maybe_record -self.callHandlers(record) + +self._tls.in_progress = True +try: +maybe_record = self.filter(record) +if not maybe_record: +return +if isinstance(maybe_record, LogRecord): +record = maybe_record +self.callHandlers(record) +finally: +self._tls.in_progress = False def addHandler(self, hdlr): """ @@ -1765,7 +1772,7 @@ def isEnabledFor(self, level): """ Is this logger enabled for level 'level'? """ -if self.disabled: +if self._is_disabled(): return False try: @@ -1815,6 +1822,11 @@ def _hierlevel(logger): if isinstance(item, Logger) and item.parent is self and _hierlevel(item) == 1 + _hierlevel(item.parent)) +def _is_disabled(self): +# We need to use getattr as it will only be set the first time a log +# message is recorded on any given thread +return self.disabled or getattr(self._tls, 'in_progress', False) + def __repr__(self): level = getLevelName(self.getEffectiveLevel()) return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level) diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 58e0381c4aa934..84ca91ad4f7331 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -4163,6 +4163,89 @@ def __init__(self, *args, **kwargs): handler = logging.getHandlerByName('custom') self.assertEqual(handler.custom_kwargs, custom_kwargs) +# See gh-91555 and gh-90321 [email protected]_subprocess() +def test_deadlock_in_queue(self): +queue = multiprocessing.Queue() +handler = logging.handlers.QueueHandler(queue) +logger = multiprocessing.get_logger() +level = logger.level +try: +logger.setLevel(logging.DEBUG) +logger.addHandler(handler) +logger.debug("deadlock") +finally: +logger.setLevel(level) +logger.removeHandler(handler) + +def test_recursion_in_custom_handler(self): +class BadHandler(logging.Handler): +def __init__(self): +super().__init__() +def emit(self, record): +logger.debug("recurse") +logger = logging.getLogger("test_recursion_in_custom_handler") +logger.addHandler(BadHandler()) +logger.setLevel(logging.DEBUG) +logger.debug("boom") + +@threading_helper.requires_working_threading() +def test_thread_supression_noninterference(self): +lock = threading.Lock() +logger = logging.getLogger("test_thread_supression_noninterference") + +# Block on the first call, allow others through +# +# NOTE: We need to bypass the base class's lock, otherwise that will +# block multiple calls to the same handler itself. +class BlockOnceHandler(TestHandler): +def __init__(self, barrier): +super().__init__(support.Matcher()) +self.barrier = barrier + +def createLock(self): +self.lock = None + +
[Python-checkins] gh-91555: disable logger while handling log record (GH-131812)
https://github.com/python/cpython/commit/2561e148ec985755baa3984b91fd0bfc089b283c
commit: 2561e148ec985755baa3984b91fd0bfc089b283c
branch: main
author: Duane Griffin
committer: vsajip
date: 2025-05-08T13:33:06+01:00
summary:
gh-91555: disable logger while handling log record (GH-131812)
Prevent the possibility of re-entrancy leading to deadlock or infinite
recursion (caused by logging triggered by logging), by disabling logging while
the logger is handling log messages.
files:
A Misc/NEWS.d/next/Library/2025-03-30-16-42-38.gh-issue-91555.ShVtwW.rst
M Lib/logging/__init__.py
M Lib/test/test_logging.py
diff --git a/Lib/logging/__init__.py b/Lib/logging/__init__.py
index aa9b79d8cab4bb..283a1055182b63 100644
--- a/Lib/logging/__init__.py
+++ b/Lib/logging/__init__.py
@@ -1474,6 +1474,8 @@ class Logger(Filterer):
level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels.
There is no arbitrary limit to the depth of nesting.
"""
+_tls = threading.local()
+
def __init__(self, name, level=NOTSET):
"""
Initialize the logger with a name and an optional level.
@@ -1670,14 +1672,19 @@ def handle(self, record):
This method is used for unpickled records received from a socket, as
well as those created locally. Logger-level filtering is applied.
"""
-if self.disabled:
-return
-maybe_record = self.filter(record)
-if not maybe_record:
+if self._is_disabled():
return
-if isinstance(maybe_record, LogRecord):
-record = maybe_record
-self.callHandlers(record)
+
+self._tls.in_progress = True
+try:
+maybe_record = self.filter(record)
+if not maybe_record:
+return
+if isinstance(maybe_record, LogRecord):
+record = maybe_record
+self.callHandlers(record)
+finally:
+self._tls.in_progress = False
def addHandler(self, hdlr):
"""
@@ -1765,7 +1772,7 @@ def isEnabledFor(self, level):
"""
Is this logger enabled for level 'level'?
"""
-if self.disabled:
+if self._is_disabled():
return False
try:
@@ -1815,6 +1822,11 @@ def _hierlevel(logger):
if isinstance(item, Logger) and item.parent is self and
_hierlevel(item) == 1 + _hierlevel(item.parent))
+def _is_disabled(self):
+# We need to use getattr as it will only be set the first time a log
+# message is recorded on any given thread
+return self.disabled or getattr(self._tls, 'in_progress', False)
+
def __repr__(self):
level = getLevelName(self.getEffectiveLevel())
return '<%s %s (%s)>' % (self.__class__.__name__, self.name, level)
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 3f113ec1be47af..1e5adcc8db13f6 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -4214,6 +4214,89 @@ def __init__(self, *args, **kwargs):
handler = logging.getHandlerByName('custom')
self.assertEqual(handler.custom_kwargs, custom_kwargs)
+# See gh-91555 and gh-90321
[email protected]_subprocess()
+def test_deadlock_in_queue(self):
+queue = multiprocessing.Queue()
+handler = logging.handlers.QueueHandler(queue)
+logger = multiprocessing.get_logger()
+level = logger.level
+try:
+logger.setLevel(logging.DEBUG)
+logger.addHandler(handler)
+logger.debug("deadlock")
+finally:
+logger.setLevel(level)
+logger.removeHandler(handler)
+
+def test_recursion_in_custom_handler(self):
+class BadHandler(logging.Handler):
+def __init__(self):
+super().__init__()
+def emit(self, record):
+logger.debug("recurse")
+logger = logging.getLogger("test_recursion_in_custom_handler")
+logger.addHandler(BadHandler())
+logger.setLevel(logging.DEBUG)
+logger.debug("boom")
+
+@threading_helper.requires_working_threading()
+def test_thread_supression_noninterference(self):
+lock = threading.Lock()
+logger = logging.getLogger("test_thread_supression_noninterference")
+
+# Block on the first call, allow others through
+#
+# NOTE: We need to bypass the base class's lock, otherwise that will
+# block multiple calls to the same handler itself.
+class BlockOnceHandler(TestHandler):
+def __init__(self, barrier):
+super().__init__(support.Matcher())
+self.barrier = barri
[Python-checkins] gh-94503: Update logging cookbook example with info on addressing log injection. (GH-136446)
https://github.com/python/cpython/commit/301b29dd306030ad4b02909d822c49cc8e05ca7d commit: 301b29dd306030ad4b02909d822c49cc8e05ca7d branch: main author: Vinay Sajip committer: vsajip date: 2025-07-09T08:30:56+01:00 summary: gh-94503: Update logging cookbook example with info on addressing log injection. (GH-136446) Co-authored-by: Stan Ulbrych <[email protected]> files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index ae2697fbce30ad..52537a91df542c 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -4140,6 +4140,42 @@ The script, when run, prints something like: 2025-07-02 13:54:47,234 DEBUG fool me ... 2025-07-02 13:54:47,234 DEBUG can't get fooled again +If, on the other hand, you are concerned about `log injection +<https://owasp.org/www-community/attacks/Log_Injection>`_, you can use a +formatter which escapes newlines, as per the following example: + +.. code-block:: python + +import logging + +logger = logging.getLogger(__name__) + +class EscapingFormatter(logging.Formatter): +def format(self, record): +s = super().format(record) +return s.replace('\n', r'\n') + +if __name__ == '__main__': +h = logging.StreamHandler() +h.setFormatter(EscapingFormatter('%(asctime)s %(levelname)-9s %(message)s')) +logging.basicConfig(level=logging.DEBUG, handlers = [h]) +logger.debug('Single line') +logger.debug('Multiple lines:\nfool me once ...') +logger.debug('Another single line') +logger.debug('Multiple lines:\n%s', 'fool me ...\ncan\'t get fooled again') + +You can, of course, use whatever escaping scheme makes the most sense for you. +The script, when run, should produce output like this: + +.. code-block:: text + +2025-07-09 06:47:33,783 DEBUG Single line +2025-07-09 06:47:33,783 DEBUG Multiple lines:\nfool me once ... +2025-07-09 06:47:33,783 DEBUG Another single line +2025-07-09 06:47:33,783 DEBUG Multiple lines:\nfool me ...\ncan't get fooled again + +Escaping behaviour can't be the stdlib default , as it would break backwards +compatibility. .. patterns-to-avoid: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
[Python-checkins] [3.14] gh-94503: Update logging cookbook example with info on addressing log injection. (GH-136446) (GH-136449)
https://github.com/python/cpython/commit/2cb579283630fca4f831622b264158b10294e4d7 commit: 2cb579283630fca4f831622b264158b10294e4d7 branch: 3.14 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-07-09T09:00:30+01:00 summary: [3.14] gh-94503: Update logging cookbook example with info on addressing log injection. (GH-136446) (GH-136449) Co-authored-by: Vinay Sajip Co-authored-by: Stan Ulbrych <[email protected]> files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index ae2697fbce30ad..52537a91df542c 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -4140,6 +4140,42 @@ The script, when run, prints something like: 2025-07-02 13:54:47,234 DEBUG fool me ... 2025-07-02 13:54:47,234 DEBUG can't get fooled again +If, on the other hand, you are concerned about `log injection +<https://owasp.org/www-community/attacks/Log_Injection>`_, you can use a +formatter which escapes newlines, as per the following example: + +.. code-block:: python + +import logging + +logger = logging.getLogger(__name__) + +class EscapingFormatter(logging.Formatter): +def format(self, record): +s = super().format(record) +return s.replace('\n', r'\n') + +if __name__ == '__main__': +h = logging.StreamHandler() +h.setFormatter(EscapingFormatter('%(asctime)s %(levelname)-9s %(message)s')) +logging.basicConfig(level=logging.DEBUG, handlers = [h]) +logger.debug('Single line') +logger.debug('Multiple lines:\nfool me once ...') +logger.debug('Another single line') +logger.debug('Multiple lines:\n%s', 'fool me ...\ncan\'t get fooled again') + +You can, of course, use whatever escaping scheme makes the most sense for you. +The script, when run, should produce output like this: + +.. code-block:: text + +2025-07-09 06:47:33,783 DEBUG Single line +2025-07-09 06:47:33,783 DEBUG Multiple lines:\nfool me once ... +2025-07-09 06:47:33,783 DEBUG Another single line +2025-07-09 06:47:33,783 DEBUG Multiple lines:\nfool me ...\ncan't get fooled again + +Escaping behaviour can't be the stdlib default , as it would break backwards +compatibility. .. patterns-to-avoid: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
[Python-checkins] [3.13] gh-94503: Update logging cookbook example with info on addressing log injection. (GH-136446) (GH-136450)
https://github.com/python/cpython/commit/301c89ced83684d854388eec4ef02cff200b7433 commit: 301c89ced83684d854388eec4ef02cff200b7433 branch: 3.13 author: Miss Islington (bot) <[email protected]> committer: vsajip date: 2025-07-09T09:00:07+01:00 summary: [3.13] gh-94503: Update logging cookbook example with info on addressing log injection. (GH-136446) (GH-136450) Co-authored-by: Vinay Sajip Co-authored-by: Stan Ulbrych <[email protected]> files: M Doc/howto/logging-cookbook.rst diff --git a/Doc/howto/logging-cookbook.rst b/Doc/howto/logging-cookbook.rst index 8ca26e44fe909b..c53be94a74009d 100644 --- a/Doc/howto/logging-cookbook.rst +++ b/Doc/howto/logging-cookbook.rst @@ -4127,6 +4127,42 @@ The script, when run, prints something like: 2025-07-02 13:54:47,234 DEBUG fool me ... 2025-07-02 13:54:47,234 DEBUG can't get fooled again +If, on the other hand, you are concerned about `log injection +<https://owasp.org/www-community/attacks/Log_Injection>`_, you can use a +formatter which escapes newlines, as per the following example: + +.. code-block:: python + +import logging + +logger = logging.getLogger(__name__) + +class EscapingFormatter(logging.Formatter): +def format(self, record): +s = super().format(record) +return s.replace('\n', r'\n') + +if __name__ == '__main__': +h = logging.StreamHandler() +h.setFormatter(EscapingFormatter('%(asctime)s %(levelname)-9s %(message)s')) +logging.basicConfig(level=logging.DEBUG, handlers = [h]) +logger.debug('Single line') +logger.debug('Multiple lines:\nfool me once ...') +logger.debug('Another single line') +logger.debug('Multiple lines:\n%s', 'fool me ...\ncan\'t get fooled again') + +You can, of course, use whatever escaping scheme makes the most sense for you. +The script, when run, should produce output like this: + +.. code-block:: text + +2025-07-09 06:47:33,783 DEBUG Single line +2025-07-09 06:47:33,783 DEBUG Multiple lines:\nfool me once ... +2025-07-09 06:47:33,783 DEBUG Another single line +2025-07-09 06:47:33,783 DEBUG Multiple lines:\nfool me ...\ncan't get fooled again + +Escaping behaviour can't be the stdlib default , as it would break backwards +compatibility. .. patterns-to-avoid: ___ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
