diff --git a/Doc/tutorial/stdlib.rst b/Doc/tutorial/stdlib.rst index dec2008add1bf50..903d8eae2b95b40 100644 --- a/Doc/tutorial/stdlib.rst +++ b/Doc/tutorial/stdlib.rst @@ -36,7 +36,7 @@ aids for working with large modules like :mod:`os`:: For daily file and directory management tasks, the :mod:`shutil` module provides -a higher level interface that is easier to use:: +a higher-level interface that is easier to use:: >>> import shutil >>> shutil.copyfile('data.db', 'archive.db') @@ -63,7 +63,7 @@ wildcard searches:: Command-line arguments ====================== -Common utility scripts often need to process command line arguments. These +Common utility scripts often need to process command-line arguments. These arguments are stored in the :mod:`sys` module's *argv* attribute as a list. For instance, let's take the following :file:`demo.py` file:: @@ -77,7 +77,7 @@ line:: ['demo.py', 'one', 'two', 'three'] The :mod:`argparse` module provides a more sophisticated mechanism to process -command line arguments. The following script extracts one or more filenames +command-line arguments. The following script extracts one or more filenames and an optional number of lines to be displayed:: import argparse diff --git a/Lib/test/test_tools/i18n_data/noheader.pot b/Lib/test/test_tools/i18n_data/noheader.pot new file mode 100644 index 000000000000000..834006e42732e56 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/noheader.pot @@ -0,0 +1,8 @@ +#: noheader.py:3 +msgid "Foo" +msgstr "" + +#: noheader.py:5 +msgid "Bar" +msgstr "" + diff --git a/Lib/test/test_tools/i18n_data/noheader.py b/Lib/test/test_tools/i18n_data/noheader.py new file mode 100644 index 000000000000000..23971799af23c67 --- /dev/null +++ b/Lib/test/test_tools/i18n_data/noheader.py @@ -0,0 +1,5 @@ +from gettext import gettext as _ + +_('Foo') + +_('Bar') diff --git a/Lib/test/test_tools/test_i18n.py b/Lib/test/test_tools/test_i18n.py index 7583b61480e3709..2dafdd3d059afaf 100644 --- a/Lib/test/test_tools/test_i18n.py +++ b/Lib/test/test_tools/test_i18n.py @@ -606,6 +606,7 @@ def extract_from_snapshots(): 'custom_keywords.py': ('--keyword=foo', '--keyword=nfoo:1,2', '--keyword=pfoo:1c,2', '--keyword=npfoo:1c,2,3', '--keyword=_:1,2'), + 'noheader.py': ('--omit-header',), 'multiple_keywords.py': ('--keyword=foo:1c,2,3', '--keyword=foo:1c,2', '--keyword=foo:1,2', # repeat a keyword to make sure it is extracted only once diff --git a/Lib/test/test_trace.py b/Lib/test/test_trace.py index 19eee19bdea6d5b..241e841d732f5ea 100644 --- a/Lib/test/test_trace.py +++ b/Lib/test/test_trace.py @@ -1,5 +1,5 @@ import os -from pickle import dump +from pickle import dump, load import sys from test.support import captured_stdout, requires_resource from test.support.os_helper import (TESTFN, rmtree, unlink) @@ -561,6 +561,28 @@ def f(): self.assertIn('lines cov% module (path)', stdout) self.assertIn(f'6 100.0% {modulename} ({filename})', stdout) + def test_count_no_report_accumulates_counts(self): + # --no-report must still save the --file counts so they accumulate. + filename = f'{TESTFN}.py' + countsfile = f'{TESTFN}.counts' + with open(filename, 'w', encoding='utf-8') as fd: + self.addCleanup(unlink, filename) + self.addCleanup(unlink, countsfile) + fd.write('for i in range(3):\n pass\n') + argv = ('-m', 'trace', '--count', '--no-report', + '--file', countsfile, filename) + assert_python_ok(*argv, PYTHONIOENCODING='utf-8') + self.assertTrue(os.path.exists(countsfile)) + with open(countsfile, 'rb') as fd: + counts = load(fd)[0] + self.assertTrue(counts) + # A second run accumulates into the same file. + assert_python_ok(*argv, PYTHONIOENCODING='utf-8') + with open(countsfile, 'rb') as fd: + accumulated = load(fd)[0] + self.assertEqual(accumulated, + {key: 2 * value for key, value in counts.items()}) + def test_run_as_module(self): assert_python_ok('-m', 'trace', '-l', '--module', 'timeit', '-n', '1') assert_python_failure('-m', 'trace', '-l', '--module', 'not_a_module_zzz') diff --git a/Lib/trace.py b/Lib/trace.py index 43ec201c4696d1d..66471f45e1c0040 100644 --- a/Lib/trace.py +++ b/Lib/trace.py @@ -287,8 +287,11 @@ def write_results(self, show_missing=True, summary=False, coverdir=None, *, n_lines, n_hits, modulename, filename = sums[m] print(f"{n_lines:5d} {n_hits/n_lines:.1%} {modulename} ({filename})") + self._save_counts() + + def _save_counts(self): + """Save the accumulated counts to ``self.outfile`` if one was given.""" if self.outfile: - # try and store counts and module info into self.outfile try: with open(self.outfile, 'wb') as f: pickle.dump((self.counts, self.calledfuncs, self.callers), @@ -744,6 +747,8 @@ def parse_ignore_dir(s): if not opts.no_report: results.write_results(opts.missing, opts.summary, opts.coverdir) + else: + results._save_counts() if __name__=='__main__': main() diff --git a/Misc/NEWS.d/next/Library/2026-06-27-10-30-00.gh-issue-152409.Tr8cE2.rst b/Misc/NEWS.d/next/Library/2026-06-27-10-30-00.gh-issue-152409.Tr8cE2.rst new file mode 100644 index 000000000000000..2e6a759a102e66c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-27-10-30-00.gh-issue-152409.Tr8cE2.rst @@ -0,0 +1,3 @@ +Fix the :mod:`trace` command-line tool not saving the ``--file`` counts +when ``--no-report`` is used, which prevented accumulating counts over +several runs. Patch by tonghuaroot. diff --git a/Misc/NEWS.d/next/Library/2026-08-19-21-01-19.gh-issue-156067.1CXkqc.rst b/Misc/NEWS.d/next/Library/2026-08-19-21-01-19.gh-issue-156067.1CXkqc.rst new file mode 100644 index 000000000000000..af4c778ece5d703 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-19-21-01-19.gh-issue-156067.1CXkqc.rst @@ -0,0 +1,2 @@ +Fix error handling in the :mod:`zoneinfo` accelerator module when a +transition index is ``-1`` or a TZ string's ``__bool__`` raises. diff --git a/Misc/NEWS.d/next/Tools-Demos/2025-02-27-19-00-00.gh-issue-130647.uwda2h.rst b/Misc/NEWS.d/next/Tools-Demos/2025-02-27-19-00-00.gh-issue-130647.uwda2h.rst new file mode 100644 index 000000000000000..4e8c1e3f883fb56 --- /dev/null +++ b/Misc/NEWS.d/next/Tools-Demos/2025-02-27-19-00-00.gh-issue-130647.uwda2h.rst @@ -0,0 +1 @@ +Add ``--omit-header`` option to :program:`pygettext`. diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 464e145438ae733..56ccabbea0ab480 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -1070,7 +1070,7 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) } Py_ssize_t cur_trans_idx = PyLong_AsSsize_t(num); - if (cur_trans_idx == -1) { + if (cur_trans_idx == -1 && PyErr_Occurred()) { goto error; } @@ -1181,7 +1181,12 @@ load_data(zoneinfo_state *state, PyZoneInfo_ZoneInfo *self, PyObject *file_obj) self->ttinfo_before = &(self->_ttinfos[0]); } - if (tz_str != Py_None && PyObject_IsTrue(tz_str)) { + int has_tz_str = PyObject_IsTrue(tz_str); + if (has_tz_str < 0) { + goto error; + } + + if (has_tz_str) { if (parse_tz_str(state, tz_str, &(self->tzrule_after))) { goto error; } diff --git a/Tools/i18n/pygettext.py b/Tools/i18n/pygettext.py index ddf4474d2bce55f..9d0fc184b84eca9 100755 --- a/Tools/i18n/pygettext.py +++ b/Tools/i18n/pygettext.py @@ -124,6 +124,15 @@ --width=columns Set width of output to columns. + --omit-header + Do not write header to file. + + This is useful for testing purposes because it eliminates a source of + variance for generated .mo files. + + Note: Using this option may lead to an error during compilation or other + manipulation if the resulting file is not entirely in ASCII. + -x filename --exclude-file=filename Specify a file that contains a list of strings that are not be @@ -629,9 +638,11 @@ def _is_string_const(self, node): def write_pot_file(messages, options, fp): timestamp = time.strftime('%Y-%m-%d %H:%M%z') encoding = fp.encoding if fp.encoding else 'UTF-8' - print(pot_header % {'time': timestamp, 'version': __version__, - 'charset': encoding, - 'encoding': '8bit'}, file=fp) + + if not options.omit_header: + print(pot_header % {'time': timestamp, 'version': __version__, + 'charset': encoding, + 'encoding': '8bit'}, file=fp) # Sort locations within each message by filename and lineno sorted_keys = [ @@ -691,7 +702,7 @@ def main(): ['extract-all', 'add-comments=?', 'default-domain=', 'escape', 'help', 'keyword=', 'no-default-keywords', 'add-location', 'no-location', 'output=', 'output-dir=', - 'style=', 'verbose', 'version', 'width=', 'exclude-file=', + 'style=', 'verbose', 'version', 'width=', 'omit-header', 'exclude-file=', 'docstrings', 'no-docstrings', ]) except getopt.error as msg: @@ -712,6 +723,7 @@ class Options: locationstyle = GNU verbose = 0 width = 78 + omit_header = False excludefilename = '' docstrings = 0 nodocstrings = {} @@ -764,6 +776,8 @@ class Options: options.width = int(arg) except ValueError: usage(1, f'--width argument must be an integer: {arg}') + elif opt in ('--omit-header',): + options.omit_header = True elif opt in ('-x', '--exclude-file'): options.excludefilename = arg elif opt in ('-X', '--no-docstrings'):