8000 Merge branch 'main' into gh-102136 · melaniearbor/cpython@7b76280 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7b76280

Browse files
committed
Merge branch 'main' into pythongh-102136
2 parents dbf7807 + 9257731 commit 7b76280

27 files changed

+681
-111
lines changed

.github/workflows/reusable-docs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ jobs:
6262
python Doc/tools/check-warnings.py \
6363
--annotate-diff '${{ env.branch_base }}' '${{ env.branch_pr }}' \
6464
--fail-if-regression \
65-
--fail-if-improved
65+
--fail-if-improved \
66+
--fail-if-new-news-nit
6667
6768
# This build doesn't use problem matchers or check annotations
6869
build_doc_oldest_supported_sphinx:

Doc/library/functions.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,13 @@ are always available. They are listed here in alphabetical order.
608608
will be used for both the global and the local variables. If *globals* and
609609
*locals* are given, they are used for the global and local variables,
610610
respectively. If provided, *locals* can be any mapping object. Remember
611-
that at the module level, globals and locals are the same dictionary. If exec
612-
gets two separate objects as *globals* and *locals*, the code will be
613-
executed as if it were embedded in a class definition.
611+
that at the module level, globals and locals are the same dictionary.
612+
613+
.. note::
614+
615+
Most users should just pass a *globals* argument and never *locals*.
616+
If exec gets two separate objects as *globals* and *locals*, the code
617+
will be executed as if it were embedded in a class definition.
614618

615619
If the *globals* dictionary does not contain a value for the key
616620
``__builtins__``, a reference to the dictionary of the built-in module

Doc/library/unittest.mock.rst

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -414,13 +414,13 @@ the *new_callable* argument to :func:`patch`.
414414

415415
This can be useful where you want to make a series of assertions that
416416
reuse the same object. Note that :meth:`reset_mock` *doesn't* clear the
417-
return value, :attr:`side_effect` or any child attributes you have
417+
:attr:`return_value`, :attr:`side_effect` or any child attributes you have
418418
set using normal assignment by default. In case you want to reset
419-
*return_value* or :attr:`side_effect`, then pass the corresponding
419+
:attr:`return_value` or :attr:`side_effect`, then pass the corresponding
420420
parameter as ``True``. Child mocks and the return value mock
421421
(if any) are reset as well.
422422

423-
.. note:: *return_value*, and :attr:`side_effect` are keyword-only
423+
.. note:: *return_value*, and *side_effect* are keyword-only
424424
arguments.
425425

426426

@@ -2584,40 +2584,16 @@ called incorrectly.
25842584

25852585
Before I explain how auto-speccing works, here's why it is needed.
25862586

2587-
:class:`Mock` is a very powerful and flexible object, but it suffers from two flaws
2588-
when used to mock out objects from a system under test. One of these flaws is
2589-
specific to the :class:`Mock` api and the other is a more general problem with using
2590-
mock objects.
2587+
:class:`Mock` is a very powerful and flexible object, but it suffers from a flaw which
2588+
is general to mocking. If you refactor some of your code, rename members and so on, any
2589+
tests for code that is still using the *old api* but uses mocks instead of the real
2590+
objects will still pass. This means your tests can all pass even though your code is
2591+
broken.
25912592

2592-
First the problem specific to :class:`Mock`. :class:`Mock` has two assert methods that are
2593-
extremely handy: :meth:`~Mock.assert_called_with` and
2594-
:meth:`~Mock.assert_called_once_with`.
2593+
.. versionchanged:: 3.5
25952594

2596-
>>> mock = Mock(name='Thing', return_value=None)
2597-
>>> mock(1, 2, 3)
2598-
>>> mock.assert_called_once_with(1, 2, 3)
2599-
>>> mock(1, 2, 3)
2600-
>>> mock.assert_called_once_with(1, 2, 3)
2601-
Traceback (most recent call last):
2602-
...
2603-
AssertionError: Expected 'mock' to be called once. Called 2 times.
2604-
2605-
Because mocks auto-create attributes on demand, and allow you to call them
2606-
with arbitrary arguments, if you misspell one of these assert methods then
2607-
your assertion is gone:
2608-
2609-
.. code-block:: pycon
2610-
2611-
>>> mock = Mock(name='Thing', return_value=None)
2612-
>>> mock(1, 2, 3)
2613-
>>> mock.assret_called_once_with(4, 5, 6) # Intentional typo!
2614-
2615-
Your tests can pass silently and incorrectly because of the typo.
2616-
2617-
The second issue is more general to mocking. If you refactor some of your
2618-
code, rename members and so on, any tests for code that is still using the
2619-
*old api* but uses mocks instead of the real objects will still pass. This
2620-
means your tests can all pass even though your code is broken.
2595+
Before 3.5, tests with a typo in the word assert would silently pass when they should
2596+
raise an error. You can still achieve this behavior by passing ``unsafe=True`` to Mock.
26212597

26222598
Note that this is another reason why you need integration tests as well as
26232599
unit tests. Testing everything in isolation is all fine and dandy, but if you

Doc/tools/check-warnings.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
from pathlib import Path
1414
from typing import TextIO
1515

16+
# Fail if NEWS nit found before this line number
17+
NEWS_NIT_THRESHOLD = 200
18+
1619
# Exclude these whether they're dirty or clean,
1720
# because they trigger a rebuild of dirty files.
1821
EXCLUDE_FILES = {
@@ -245,6 +248,32 @@ def fail_if_improved(
245248
return 0
246249

247250

251+
def fail_if_new_news_nit(warnings: list[str], threshold: int) -> int:
252+
"""
253+
Ensure no warnings are found in the NEWS file before a given line number.
254+
"""
255+
news_nits = (
256+
warning
257+
for warning in warnings
258+
if "/build/NEWS:" in warning
259+
)
260+
261+
# Nits found before the threshold line
262+
new_news_nits = [
263+
nit
264+
for nit in news_nits
265+
if int(nit.split(":")[1]) <= threshold
266+
]
267+
268+
if new_news_nits:
269+
print("\nError: new NEWS nits:\n")
270+
for warning in new_news_nits:
271+
print(warning)
272+
return -1
273+
274+
return 0
275+
276+
248277
def main(argv: list[str] | None = None) -> int:
249278
parser = argparse.ArgumentParser()
250279
parser.add_argument(
@@ -264,6 +293,14 @@ def main(argv: list[str] | None = None) -> int:
264293
action="store_true",
265294
help="Fail if new files with no nits are found",
266295
)
296+
parser.add_argument(
297+
"--fail-if-new-news-nit",
298+
metavar="threshold",
299+
type=int,
300+
nargs="?",
301+
const=NEWS_NIT_THRESHOLD,
302+
help="Fail if new NEWS nit found before threshold line number",
303+
)
267304

268305
args = parser.parse_args(argv)
269306
if args.annotate_diff is not None and len(args.annotate_diff) > 2:
@@ -304,6 +341,9 @@ def main(argv: list[str] | None = None) -> int:
304341
if args.fail_if_improved:
305342
exit_code += fail_if_improved(files_with_expected_nits, files_with_nits)
306343

344+
if args.fail_if_new_news_nit:
345+
exit_code += fail_if_new_news_nit(warnings, args.fail_if_new_news_nit)
346+
307347
return exit_code
308348

309349

Doc/whatsnew/3.13.rst

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,16 +2177,6 @@ Changes in the Python API
21772177
returned by :meth:`zipfile.ZipFile.open` was changed from ``'r'`` to ``'rb'``.
21782178
(Contributed by Serhiy Storchaka in :gh:`115961`.)
21792179

2180-
* Callbacks registered in the :mod:`tkinter` module now take arguments as
2181-
various Python objects (``int``, ``float``, ``bytes``, ``tuple``),
2182-
not just ``str``.
2183-
To restore the previous behavior set :mod:`!tkinter` module global
2184-
:data:`!wantobject` to ``1`` before creating the
2185-
:class:`!Tk` object or call the :meth:`!wantobject`
2186-
method of the :class:`!Tk` object with argument ``1``.
2187-
Calling it with argument ``2`` restores the current default behavior.
2188-
(Contributed by Serhiy Storchaka in :gh:`66410`.)
2189-
21902180

21912181
Changes in the C API
21922182
--------------------

Lib/_ios_support.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
# ctypes is an optional module. If it's not present, we're limited in what
66
# we can tell about the system, but we don't want to prevent the module
77
# from working.
8-
print("ctypes isn't available; iOS system calls will not be available")
8+
print("ctypes isn't available; iOS system calls will not be available", file=sys.stderr)
99
objc = None
1010
else:
1111
# ctypes is available. Load the ObjC library, and wrap the objc_getClass,
1212
# sel_registerName methods
1313
lib = util.find_library("objc")
1414
if lib is None:
1515
# Failed to load the objc library
16-
raise RuntimeError("ObjC runtime library couldn't be loaded")
16+
raise ImportError("ObjC runtime library couldn't be loaded")
1717

1818
objc = cdll.LoadLibrary(lib)
1919
objc.objc_getClass.restype = c_void_p

Lib/_pyrepl/commands.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@
3434

3535
# types
3636
if False:
37-
from .reader import Reader
3837
from .historical_reader import HistoricalReader
39-
from .console import Event
4038

4139

4240
class Command:
@@ -245,7 +243,7 @@ def do(self) -> None:
245243
x, y = r.pos2xy()
246244
new_y = y - 1
247245

248-
if new_y < 0:
246+
if r.bol() == 0:
249247
if r.historyi > 0:
250248
r.select_item(r.historyi - 1)
251249
return

Lib/email/_header_value_parser.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,11 +2784,15 @@ def _refold_parse_tree(parse_tree, *, policy):
27842784
# max_line_length 0/None means no limit, ie: infinitely long.
27852785
maxlen = policy.max_line_length or sys.maxsize
27862786
encoding = 'utf-8' if policy.utf8 else 'us-ascii'
2787-
lines = ['']
2788-
last_ew = None
2787+
lines = [''] # Folded lines to be output
2788+
leading_whitespace = '' # When we have whitespace between two encoded
2789+
# words, we may need to encode the whitespace
2790+
# at the beginning of the second word.
2791+
last_ew = None # Points to the last encoded character if there's an ew on
2792+
# the line
27892793
last_charset = None
27902794
wrap_as_ew_blocked = 0
2791-
want_encoding = False
2795+
want_encoding = False # This is set to True if we need to encode this part
27922796
end_ew_not_allowed = Terminal('', 'wrap_as_ew_blocked')
27932797
parts = list(parse_tree)
27942798
while parts:
@@ -2812,10 +2816,12 @@ def _refold_parse_tree(parse_tree, *, policy):
28122816
# 'charset' property on the policy.
28132817
charset = 'utf-8'
28142818
want_encoding = True
2819+
28152820
if part.token_type == 'mime-parameters':
28162821
# Mime parameter folding (using RFC2231) is extra special.
28172822
_fold_mime_parameters(part, lines, maxlen, encoding)
28182823
continue
2824+
28192825
if want_encoding and not wrap_as_ew_blocked:
28202826
if not part.as_ew_allowed:
28212827
want_encoding = False
@@ -2847,21 +2853,38 @@ def _refold_parse_tree(parse_tree, *, policy):
28472853
last_charset == 'utf-8' and charset != 'us-ascii')):
28482854
last_ew = None
28492855
last_ew = _fold_as_ew(tstr, lines, maxlen, last_ew,
2850-
part.ew_combine_allowed, charset)
2856+
part.ew_combine_allowed, charset, leading_whitespace)
2857+
# This whitespace has been added to the lines in _fold_as_ew()
2858+
# so clear it now.
2859+
leading_whitespace = ''
28512860
last_charset = charset
28522861
want_encoding = False
28532862
continue
2863+
28542864
if len(tstr) <= maxlen - len(lines[-1]):
28552865
lines[-1] += tstr
28562866
continue
2867+
28572868
# This part is too long to fit. The RFC wants us to break at
28582869
# "major syntactic breaks", so unless we don't consider this
28592870
# to be one, check if it will fit on the next line by itself.
2871+
leading_whitespace = ''
28602872
if (part.syntactic_break and
28612873
len(tstr) + 1 <= maxlen):
28622874
newline = _steal_trailing_WSP_if_exists(lines)
28632875
if newline or part.startswith_fws():
2876+
# We're going to fold the data onto a new line here. Due to
2877+
# the way encoded strings handle continuation lines, we need to
2878+
# be prepared to encode any whitespace if the next line turns
2879+
# out to start with an encoded word.
28642880
lines.append(newline + tstr)
2881+
2882+
whitespace_accumulator = []
2883+
for char in lines[-1]:
2884+
if char not in WSP:
2885+
break
2886+
whitespace_accumulator.append(char)
2887+
leading_whitespace = ''.join(whitespace_accumulator)
28652888
last_ew = None
28662889
continue
28672890
if not hasattr(part, 'encode'):
@@ -2885,9 +2908,10 @@ def _refold_parse_tree(parse_tree, *, policy):
28852908
else:
28862909
# We can't fold it onto the next line either...
28872910
lines[-1] += tstr
2911+
28882912
return policy.linesep.join(lines) + policy.linesep
28892913

2890-
def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
2914+
def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset, leading_whitespace):
28912915
"""Fold string to_encode into lines as encoded word, combining if allowed.
28922916
Return the new value for last_ew, or None if ew_combine_allowed is False.
28932917
@@ -2902,14 +2926,15 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
29022926
to_encode = str(
29032927
get_unstructured(lines[-1][last_ew:] + to_encode))
29042928
lines[-1] = lines[-1][:last_ew]
2905-
if to_encode[0] in WSP:
2929+
elif to_encode[0] in WSP:
29062930
# We're joining this to non-encoded text, so don't encode
29072931
# the leading blank.
29082932
leading_wsp = to_encode[0]
29092933
to_encode = to_encode[1:]
29102934
if (len(lines[-1]) == maxlen):
29112935
lines.append(_steal_trailing_WSP_if_exists(lines))
29122936
lines[-1] += leading_wsp
2937+
29132938
trailing_wsp = ''
29142939
if to_encode[-1] in WSP:
29152940
# Likewise for the trailing space.
@@ -2929,11 +2954,20 @@ def _fold_as_ew(to_encode, lines, maxlen, last_ew, ew_combine_allowed, charset):
29292954

29302955
while to_encode:
29312956
remaining_space = maxlen - len(lines[-1])
2932-
text_space = remaining_space - chrome_len
2957+
text_space = remaining_space - chrome_len - len(leading_whitespace)
29332958
if text_space <= 0:
29342959
lines.append(' ')
29352960
continue
29362961

2962+
# If we are at the start of a continuation line, prepend whitespace
2963+
# (we only want to do this when the line starts with an encoded word
2964+
# but if we're folding in this helper function, then we know that we
2965+
# are going to be writing out an encoded word.)
2966+
if len(lines) > 1 and len(lines[-1]) == 1 and leading_whitespace:
2967+
encoded_word = _ew.encode(leading_whitespace, charset=encode_as)
2968+
lines[-1] += encoded_word
2969+
leading_whitespace = ''
2970+
29372971
to_encode_word = to_encode[:text_space]
29382972
encoded_word = _ew.encode(to_encode_word, charset=encode_as)
29392973
excess = len(encoded_word) - remaining_space

Lib/test/libregrtest/cmdline.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -520,15 +520,6 @@ def _parse_args(args, **kwargs):
520520
"--huntrleaks without -jN option",
521521
file=sys.stderr)
522522

523-
if ns.huntrleaks and ns.xmlpath:
524-
# The XML data is written into a file outside runtest_refleak(), so
525-
# it looks like a leak but it's not. Simply disable XML output when
526-
# hunting for reference leaks (gh-83434).
527-
ns.xmlpath = None
528-
print("WARNING: Disable --junit-xml because it's incompatible "
529-
"with --huntrleaks",
530-
file=sys.stderr)
531-
532523
if ns.forever:
533524
# --forever implies --failfast
534525
ns.failfast = True

0 commit comments

Comments
 (0)
0