Skip to content

Commit 154340c

Browse files
committed
Merge main; restore dropped tests, use isdigit() for the fraction, drop the redundant length check, and add per-component tests
2 parents 9476ebc + e8158d1 commit 154340c

13 files changed

Lines changed: 102 additions & 20 deletions

Doc/tutorial/stdlib.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ aids for working with large modules like :mod:`os`::
3636
<returns an extensive manual page created from the module's docstrings>
3737

3838
For daily file and directory management tasks, the :mod:`shutil` module provides
39-
a higher level interface that is easier to use::
39+
a higher-level interface that is easier to use::
4040

4141
>>> import shutil
4242
>>> shutil.copyfile('data.db', 'archive.db')
@@ -63,7 +63,7 @@ wildcard searches::
6363
Command-line arguments
6464
======================
6565

66-
Common utility scripts often need to process command line arguments. These
66+
Common utility scripts often need to process command-line arguments. These
6767
arguments are stored in the :mod:`sys` module's *argv* attribute as a list. For
6868
instance, let's take the following :file:`demo.py` file::
6969

@@ -77,7 +77,7 @@ line::
7777
['demo.py', 'one', 'two', 'three']
7878

7979
The :mod:`argparse` module provides a more sophisticated mechanism to process
80-
command line arguments. The following script extracts one or more filenames
80+
command-line arguments. The following script extracts one or more filenames
8181
and an optional number of lines to be displayed::
8282

8383
import argparse

Lib/_pydatetime.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,6 @@ def _parse_hh_mm_ss_ff(tstr):
413413
time_comps = [0, 0, 0, 0]
414414
pos = 0
415415
for comp in range(0, 3):
416-
if (len_str - pos) < 2:
417-
raise ValueError("Incomplete time component")
418-
419416
time_comps[comp] = _read_isoformat_component(tstr[pos:pos+2], 2)
420417

421418
pos += 2
@@ -437,7 +434,7 @@ def _parse_hh_mm_ss_ff(tstr):
437434
raise ValueError("Invalid microsecond separator")
438435
else:
439436
pos += 1
440-
if not all(map(_is_ascii_digit, tstr[pos:])):
437+
if not tstr[pos:].isdigit():
441438
raise ValueError("Non-digit values in fraction")
442439

443440
len_remainder = len_str - pos

Lib/test/datetimetester.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,8 +2107,8 @@ def test_fromisoformat_fails(self):
21072107
'2020-W25-0', # Invalid day-of-week
21082108
'2020-W25-8', # Invalid day-of-week
21092109
# gh-152204: each fixed-width field must be exactly N ASCII digits
2110-
'2020+12', # '+' accepted in a basic-format field
2111-
'2020 12', # space accepted in a basic-format field
2110+
'2020+12', # '+' in a basic-format field
2111+
'2020 12', # space in a basic-format field
21122112
'+020-06-15', # leading sign in the year
21132113
'202012+9', # '+' in the day field
21142114
'2020-W 5', # space in the week day-of-week field
@@ -3766,9 +3766,19 @@ def test_fromisoformat_fails_datetime(self):
37663766
'2009-04-19T12:30:45-00:90:00', # Time zone field out from range
37673767
'2009-04-19T12:30:45-00:00:90', # Time zone field out from range
37683768
'2020-2020', # Ambiguous 9-char date portion
3769-
'2009-04-19T12:30:4٥', # Unicode digit in the seconds
3770-
'20201212T0102٣٤', # Unicode digits in the time (gh-152204)
3771-
'2009-04-19T12:30:45+0٥:00', # Unicode digit in the timezone
3769+
# gh-152204: each time field must be exactly N ASCII digits
3770+
'2020-12-12T0٥:02:03', # Unicode digit in the hour
3771+
'2020-12-12T01:0٥:03', # Unicode digit in the minute
3772+
'2020-12-12T01:02:0٥', # Unicode digit in the second
3773+
'2020-12-12T01:02:03.٥', # Unicode digit in the fraction
3774+
'2020-12-12T01:02:03.4_6', # underscore in the fraction
3775+
'2020-12-12T01:02:03+0٥:00', # Unicode digit in the tz hour
3776+
'2020-12-12T01:02:03+01:0٥', # Unicode digit in the tz minute
3777+
'20201212T0102٣٤', # Unicode digits in the basic-format time
3778+
'2009-04-19T12:30:45.+05:00', # Empty fraction before offset
3779+
'2009-04-19T12:30:45.-05:00', # Empty fraction before offset
3780+
'2009-04-19T12:30:45.Z', # Empty fraction before Z
3781+
'2009-04-19T12:30:45,+05:00', # Empty fraction (comma) before offset
37723782
]
37733783

37743784
for bad_str in bad_strs:
@@ -4130,6 +4140,11 @@ def test_strftime_special(self):
41304140
self.assertEqual(t.strftime('\0'*1000), '\0'*1000)
41314141
self.assertEqual(t.strftime('\0%I%p%Z\0%X'), f'\0{s1}\0{s2}')
41324142
self.assertEqual(t.strftime('%I%p%Z\0%X\0'), f'{s1}\0{s2}\0')
4143+
# gh-152305: the year directives must not raise on a time.
4144+
for directive, expected in (('%Y', '1900'), ('%G', '1900'),
4145+
('%C', '19'), ('%F', '1900-01-01')):
4146+
with self.subTest(directive=directive):
4147+
self.assertEqual(t.strftime(directive), expected)
41334148

41344149
def test_format(self):
41354150
t = self.theclass(1, 2, 3, 4)
@@ -5040,6 +5055,10 @@ def test_fromisoformat_fails(self):
50405055
'24:01:00.000000', # Has non-zero minutes on 24:00
50415056
'12:30:45+00:90:00', # Time zone field out from range
50425057
'12:30:45+00:00:90', # Time zone field out from range
5058+
'12:30:45.+05:00', # Empty fraction before offset
5059+
'12:30:45.-05:00', # Empty fraction before offset
5060+
'12:30:45.Z', # Empty fraction before Z
5061+
'12:30:45,+05:00', # Empty fraction (comma) before offset
50435062
]
50445063

50455064
for bad_str in bad_strs:
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#: noheader.py:3
2+
msgid "Foo"
3+
msgstr ""
4+
5+
#: noheader.py:5
6+
msgid "Bar"
7+
msgstr ""
8+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from gettext import gettext as _
2+
3+
_('Foo')
4+
5+
_('Bar')

Lib/test/test_tools/test_i18n.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ def extract_from_snapshots():
606606
'custom_keywords.py': ('--keyword=foo', '--keyword=nfoo:1,2',
607607
'--keyword=pfoo:1c,2',
608608
'--keyword=npfoo:1c,2,3', '--keyword=_:1,2'),
609+
'noheader.py': ('--omit-header',),
609610
'multiple_keywords.py': ('--keyword=foo:1c,2,3', '--keyword=foo:1c,2',
610611
'--keyword=foo:1,2',
611612
# repeat a keyword to make sure it is extracted only once

Lib/test/test_trace.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from pickle import dump
2+
from pickle import dump, load
33
import sys
44
from test.support import captured_stdout, requires_resource
55
from test.support.os_helper import (TESTFN, rmtree, unlink)
@@ -561,6 +561,28 @@ def f():
561561
self.assertIn('lines cov% module (path)', stdout)
562562
self.assertIn(f'6 100.0% {modulename} ({filename})', stdout)
563563

564+
def test_count_no_report_accumulates_counts(self):
565+
# --no-report must still save the --file counts so they accumulate.
566+
filename = f'{TESTFN}.py'
567+
countsfile = f'{TESTFN}.counts'
568+
with open(filename, 'w', encoding='utf-8') as fd:
569+
self.addCleanup(unlink, filename)
570+
self.addCleanup(unlink, countsfile)
571+
fd.write('for i in range(3):\n pass\n')
572+
argv = ('-m', 'trace', '--count', '--no-report',
573+
'--file', countsfile, filename)
574+
assert_python_ok(*argv, PYTHONIOENCODING='utf-8')
575+
self.assertTrue(os.path.exists(countsfile))
576+
with open(countsfile, 'rb') as fd:
577+
counts = load(fd)[0]
578+
self.assertTrue(counts)
579+
# A second run accumulates into the same file.
580+
assert_python_ok(*argv, PYTHONIOENCODING='utf-8')
581+
with open(countsfile, 'rb') as fd:
582+
accumulated = load(fd)[0]
583+
self.assertEqual(accumulated,
584+
{key: 2 * value for key, value in counts.items()})
585+
564586
def test_run_as_module(self):
565587
assert_python_ok('-m', 'trace', '-l', '--module', 'timeit', '-n', '1')
566588
assert_python_failure('-m', 'trace', '-l', '--module', 'not_a_module_zzz')

Lib/trace.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,11 @@ def write_results(self, show_missing=True, summary=False, coverdir=None, *,
287287
n_lines, n_hits, modulename, filename = sums[m]
288288
print(f"{n_lines:5d} {n_hits/n_lines:.1%} {modulename} ({filename})")
289289

290+
self._save_counts()
291+
292+
def _save_counts(self):
293+
"""Save the accumulated counts to ``self.outfile`` if one was given."""
290294
if self.outfile:
291-
# try and store counts and module info into self.outfile
292295
try:
293296
with open(self.outfile, 'wb') as f:
294297
pickle.dump((self.counts, self.calledfuncs, self.callers),
@@ -744,6 +747,8 @@ def parse_ignore_dir(s):
744747

745748
if not opts.no_report:
746749
results.write_results(opts.missing, opts.summary, opts.coverdir)
750+
else:
751+
results._save_counts()
747752

748753
if __name__=='__main__':
749754
main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix the :mod:`trace` command-line tool not saving the ``--file`` counts
2+
when ``--no-report`` is used, which prevented accumulating counts over
3+
several runs. Patch by tonghuaroot.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix error handling in the :mod:`zoneinfo` accelerator module when a
2+
transition index is ``-1`` or a TZ string's ``__bool__`` raises.

0 commit comments

Comments
 (0)