Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2f72974

Browse filesBrowse files
authored
Merge branch 'main' into closed-loop-runtime-error
2 parents 075a7fd + 12a30bc commit 2f72974
Copy full SHA for 2f72974

File tree

8 files changed

+255
-4
lines changed
Filter options

8 files changed

+255
-4
lines changed

‎Doc/library/mailbox.rst

Copy file name to clipboardExpand all lines: Doc/library/mailbox.rst
+103-1Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,108 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF.
424424
remove the underlying message while the returned file remains open.
425425

426426

427+
.. method:: get_flags(key)
428+
429+
Return as a string the flags that are set on the message
430+
corresponding to *key*.
431+
This is the same as ``get_message(key).get_flags()`` but much
432+
faster, because it does not open the message file.
433+
Use this method when iterating over the keys to determine which
434+
messages are interesting to get.
435+
436+
If you do have a :class:`MaildirMessage` object, use
437+
its :meth:`~MaildirMessage.get_flags` method instead, because
438+
changes made by the message's :meth:`~MaildirMessage.set_flags`,
439+
:meth:`~MaildirMessage.add_flag` and :meth:`~MaildirMessage.remove_flag`
440+
methods are not reflected here until the mailbox's
441+
:meth:`__setitem__` method is called.
442+
443+
.. versionadded:: 3.13
444+
445+
446+
.. method:: set_flags(key, flags)
447+
448+
On the message corresponding to *key*, set the flags specified
449+
by *flags* and unset all others.
450+
Calling ``some_mailbox.set_flags(key, flags)`` is similar to ::
451+
452+
one_message = some_mailbox.get_message(key)
453+
one_message.set_flags(flags)
454+
some_mailbox[key] = one_message
455+
456+
but faster, because it does not open the message file.
457+
458+
If you do have a :class:`MaildirMessage` object, use
459+
its :meth:`~MaildirMessage.set_flags` method instead, because
460+
changes made with this mailbox method will not be visible to the
461+
message object's method, :meth:`~MaildirMessage.get_flags`.
462+
463+
.. versionadded:: 3.13
464+
465+
466+
.. method:: add_flag(key, flag)
467+
468+
On the message corresponding to *key*, set the flags specified
469+
by *flag* without changing other flags. To add more than one
470+
flag at a time, *flag* may be a string of more than one character.
471+
472+
Considerations for using this method versus the message object's
473+
:meth:`~MaildirMessage.add_flag` method are similar to
474+
those for :meth:`set_flags`; see the discussion there.
475+
476+
.. versionadded:: 3.13
477+
478+
479+
.. method:: remove_flag(key, flag)
480+
481+
On the message corresponding to *key*, unset the flags specified
482+
by *flag* without changing other flags. To remove more than one
483+
flag at a time, *flag* may be a string of more than one character.
484+
485+
Considerations for using this method versus the message object's
486+
:meth:`~MaildirMessage.remove_flag` method are similar to
487+
those for :meth:`set_flags`; see the discussion there.
488+
489+
.. versionadded:: 3.13
490+
491+
492+
.. method:: get_info(key)
493+
494+
Return a string containing the info for the message
495+
corresponding to *key*.
496+
This is the same as ``get_message(key).get_info()`` but much
497+
faster, because it does not open the message file.
498+
Use this method when iterating over the keys to determine which
499+
messages are interesting to get.
500+
501+
If you do have a :class:`MaildirMessage` object, use
502+
its :meth:`~MaildirMessage.get_info` method instead, because
503+
changes made by the message's :meth:`~MaildirMessage.set_info` method
504+
are not reflected here until the mailbox's :meth:`__setitem__` method
505+
is called.
506+
507+
.. versionadded:: 3.13
508+
509+
510+
.. method:: set_info(key, info)
511+
512+
Set the info of the message corresponding to *key* to *info*.
513+
Calling ``some_mailbox.set_info(key, flags)`` is similar to ::
514+
515+
one_message = some_mailbox.get_message(key)
516+
one_message.set_info(info)
517+
some_mailbox[key] = one_message
518+
519+
but faster, because it does not open the message file.
520+
521+
If you do have a :class:`MaildirMessage` object, use
522+
its :meth:`~MaildirMessage.set_info` method instead, because
523+
changes made with this mailbox method will not be visible to the
524+
message object's method, :meth:`~MaildirMessage.get_info`.
525+
526+
.. versionadded:: 3.13
527+
528+
427529
.. seealso::
428530

429531
`maildir man page from Courier <https://www.courier-mta.org/maildir.html>`_
@@ -838,7 +940,7 @@ Supported mailbox formats are Maildir, mbox, MH, Babyl, and MMDF.
838940
.. note::
839941

840942
A message is typically moved from :file:`new` to :file:`cur` after its
841-
mailbox has been accessed, whether or not the message is has been
943+
mailbox has been accessed, whether or not the message has been
842944
read. A message ``msg`` has been read if ``"S" in msg.get_flags()`` is
843945
``True``.
844946

‎Lib/mailbox.py

Copy file name to clipboardExpand all lines: Lib/mailbox.py
+50Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,56 @@ def get_file(self, key):
395395
f = open(os.path.join(self._path, self._lookup(key)), 'rb')
396396
return _ProxyFile(f)
397397

398+
def get_info(self, key):
399+
"""Get the keyed message's "info" as a string."""
400+
subpath = self._lookup(key)
401+
if self.colon in subpath:
402+
return subpath.split(self.colon)[-1]
403+
return ''
404+
405+
def set_info(self, key, info: str):
406+
"""Set the keyed message's "info" string."""
407+
if not isinstance(info, str):
408+
raise TypeError(f'info must be a string: {type(info)}')
409+
old_subpath = self._lookup(key)
410+
new_subpath = old_subpath.split(self.colon)[0]
411+
if info:
412+
new_subpath += self.colon + info
413+
if new_subpath == old_subpath:
414+
return
415+
old_path = os.path.join(self._path, old_subpath)
416+
new_path = os.path.join(self._path, new_subpath)
417+
os.rename(old_path, new_path)
418+
self._toc[key] = new_subpath
419+
420+
def get_flags(self, key):
421+
"""Return as a string the standard flags that are set on the keyed message."""
422+
info = self.get_info(key)
423+
if info.startswith('2,'):
424+
return info[2:]
425+
return ''
426+
427+
def set_flags(self, key, flags: str):
428+
"""Set the given flags and unset all others on the keyed message."""
429+
if not isinstance(flags, str):
430+
raise TypeError(f'flags must be a string: {type(flags)}')
431+
# TODO: check if flags are valid standard flag characters?
432+
self.set_info(key, '2,' + ''.join(sorted(set(flags))))
433+
434+
def add_flag(self, key, flag: str):
435+
"""Set the given flag(s) without changing others on the keyed message."""
436+
if not isinstance(flag, str):
437+
raise TypeError(f'flag must be a string: {type(flag)}')
438+
# TODO: check that flag is a valid standard flag character?
439+
self.set_flags(key, ''.join(set(self.get_flags(key)) | set(flag)))
440+
441+
def remove_flag(self, key, flag: str):
442+
"""Unset the given string flag(s) without changing others on the keyed message."""
443+
if not isinstance(flag, str):
444+
raise TypeError(f'flag must be a string: {type(flag)}')
445+
if self.get_flags(key):
446+
self.set_flags(key, ''.join(set(self.get_flags(key)) - set(flag)))
447+
398448
def iterkeys(self):
399449
"""Return an iterator over keys."""
400450
self._refresh()

‎Lib/test/test_mailbox.py

Copy file name to clipboardExpand all lines: Lib/test/test_mailbox.py
+86Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,92 @@ def test_lock_unlock(self):
847847
self._box.lock()
848848
self._box.unlock()
849849

850+
def test_get_info(self):
851+
# Test getting message info from Maildir, not the message.
852+
msg = mailbox.MaildirMessage(self._template % 0)
853+
key = self._box.add(msg)
854+
self.assertEqual(self._box.get_info(key), '')
855+
msg.set_info('OurTestInfo')
856+
self._box[key] = msg
857+
self.assertEqual(self._box.get_info(key), 'OurTestInfo')
858+
859+
def test_set_info(self):
860+
# Test setting message info from Maildir, not the message.
861+
# This should immediately rename the message file.
862+
msg = mailbox.MaildirMessage(self._template % 0)
863+
key = self._box.add(msg)
864+
def check_info(oldinfo, newinfo):
865+
oldfilename = os.path.join(self._box._path, self._box._lookup(key))
866+
newsubpath = self._box._lookup(key).split(self._box.colon)[0]
867+
if newinfo:
868+
newsubpath += self._box.colon + newinfo
869+
newfilename = os.path.join(self._box._path, newsubpath)
870+
# assert initial conditions
871+
self.assertEqual(self._box.get_info(key), oldinfo)
872+
if not oldinfo:
873+
self.assertNotIn(self._box._lookup(key), self._box.colon)
874+
self.assertTrue(os.path.exists(oldfilename))
875+
if oldinfo != newinfo:
876+
self.assertFalse(os.path.exists(newfilename))
877+
# do the rename
878+
self._box.set_info(key, newinfo)
879+
# assert post conditions
880+
if not newinfo:
881+
self.assertNotIn(self._box._lookup(key), self._box.colon)
882+
if oldinfo != newinfo:
883+
self.assertFalse(os.path.exists(oldfilename))
884+
self.assertTrue(os.path.exists(newfilename))
885+
self.assertEqual(self._box.get_info(key), newinfo)
886+
# none -> has info
887+
check_info('', 'info1')
888+
# has info -> same info
889+
check_info('info1', 'info1')
890+
# has info -> different info
891+
check_info('info1', 'info2')
892+
# has info -> none
893+
check_info('info2', '')
894+
# none -> none
895+
check_info('', '')
896+
897+
def test_get_flags(self):
898+
# Test getting message flags from Maildir, not the message.
899+
msg = mailbox.MaildirMessage(self._template % 0)
900+
key = self._box.add(msg)
901+
self.assertEqual(self._box.get_flags(key), '')
902+
msg.set_flags('T')
903+
self._box[key] = msg
904+
self.assertEqual(self._box.get_flags(key), 'T')
905+
906+
def test_set_flags(self):
907+
msg = mailbox.MaildirMessage(self._template % 0)
908+
key = self._box.add(msg)
909+
self.assertEqual(self._box.get_flags(key), '')
910+
self._box.set_flags(key, 'S')
911+
self.assertEqual(self._box.get_flags(key), 'S')
912+
913+
def test_add_flag(self):
914+
msg = mailbox.MaildirMessage(self._template % 0)
915+
key = self._box.add(msg)
916+
self.assertEqual(self._box.get_flags(key), '')
917+
self._box.add_flag(key, 'B')
918+
self.assertEqual(self._box.get_flags(key), 'B')
919+
self._box.add_flag(key, 'B')
920+
self.assertEqual(self._box.get_flags(key), 'B')
921+
self._box.add_flag(key, 'AC')
922+
self.assertEqual(self._box.get_flags(key), 'ABC')
923+
924+
def test_remove_flag(self):
925+
msg = mailbox.MaildirMessage(self._template % 0)
926+
key = self._box.add(msg)
927+
self._box.set_flags(key, 'abc')
928+
self.assertEqual(self._box.get_flags(key), 'abc')
929+
self._box.remove_flag(key, 'b')
930+
self.assertEqual(self._box.get_flags(key), 'ac')
931+
self._box.remove_flag(key, 'b')
932+
self.assertEqual(self._box.get_flags(key), 'ac')
933+
self._box.remove_flag(key, 'ac')
934+
self.assertEqual(self._box.get_flags(key), '')
935+
850936
def test_folder (self):
851937
# Test for bug #1569790: verify that folders returned by .get_folder()
852938
# use the same factory function.

‎Misc/ACKS

Copy file name to clipboardExpand all lines: Misc/ACKS
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ Dinu Gherman
630630
Subhendu Ghosh
631631
Jonathan Giddy
632632
Johannes Gijsbers
633+
Stephen Gildea
633634
Michael Gilfix
634635
Julian Gindi
635636
Yannick Gingras
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
New methods :meth:`mailbox.Maildir.get_info`,
2+
:meth:`mailbox.Maildir.set_info`, :meth:`mailbox.Maildir.get_flags`,
3+
:meth:`mailbox.Maildir.set_flags`, :meth:`mailbox.Maildir.add_flag`,
4+
:meth:`mailbox.Maildir.remove_flag`. These methods speed up accessing a
5+
message's info and/or flags and are useful when it is not necessary to
6+
access the message's contents, as when iterating over a Maildir to find
7+
messages with specific flags.

‎Modules/_datetimemodule.c

Copy file name to clipboardExpand all lines: Modules/_datetimemodule.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4630,7 +4630,7 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) {
46304630
}
46314631

46324632
int hour = 0, minute = 0, second = 0, microsecond = 0;
4633-
int tzoffset, tzimicrosecond = 0;
4633+
int tzoffset = 0, tzimicrosecond = 0;
46344634
int rv = parse_isoformat_time(p, len,
46354635
&hour, &minute, &second, &microsecond,
46364636
&tzoffset, &tzimicrosecond);

‎Modules/gcmodule.c

Copy file name to clipboardExpand all lines: Modules/gcmodule.c
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2396,14 +2396,16 @@ PyObject_GC_Del(void *op)
23962396
size_t presize = _PyType_PreHeaderSize(((PyObject *)op)->ob_type);
23972397
PyGC_Head *g = AS_GC(op);
23982398
if (_PyObject_GC_IS_TRACKED(op)) {
2399+
gc_list_remove(g);
23992400
#ifdef Py_DEBUG
2401+
PyObject *exc = PyErr_GetRaisedException();
24002402
if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0,
24012403
"gc", NULL, "Object of type %s is not untracked before destruction",
24022404
((PyObject*)op)->ob_type->tp_name)) {
24032405
PyErr_WriteUnraisable(NULL);
24042406
}
2407+
PyErr_SetRaisedException(exc);
24052408
#endif
2406-
gc_list_remove(g);
24072409
}
24082410
GCState *gcstate = get_gc_state();
24092411
if (gcstate->generations[0].count > 0) {

‎Python/bltinmodule.c

Copy file name to clipboardExpand all lines: Python/bltinmodule.c
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2611,7 +2611,10 @@ builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
26112611
}
26122612
if (PyFloat_CheckExact(item)) {
26132613
// Improved Kahan–Babuška algorithm by Arnold Neumaier
2614-
// https://www.mat.univie.ac.at/~neum/scan/01.pdf
2614+
// Neumaier, A. (1974), Rundungsfehleranalyse einiger Verfahren
2615+
// zur Summation endlicher Summen. Z. angew. Math. Mech.,
2616+
// 54: 39-51. https://doi.org/10.1002/zamm.19740540106
2617+
// https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements
26152618
double x = PyFloat_AS_DOUBLE(item);
26162619
double t = f_result + x;
26172620
if (fabs(f_result) >= fabs(x)) {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.