Skip to content

Commit 0a8640e

Browse files
committed
minor fixes; message, doc polish
1 parent f78d8d3 commit 0a8640e

File tree

5 files changed

+36
-19
lines changed

5 files changed

+36
-19
lines changed

Doc/library/functools.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ The :mod:`functools` module defines the following functions:
348348

349349
The :func:`partial` is used for partial function application which "freezes"
350350
some portion of a function's arguments and/or keywords resulting in a new object
351-
with a simplified signature. For example, :func:`partial` can be used to create
351+
with a simplified signature. For example, :func:`partial` can be used to create
352352
a callable that behaves like the :func:`int` function where the *base* argument
353353
defaults to two:
354354

@@ -362,7 +362,7 @@ The :mod:`functools` module defines the following functions:
362362
when :func:`partial` is called. This allows custom selection of positional arguments
363363
to be pre-filled when constructing a :ref:`partial object <partial-objects>`.
364364

365-
If :data:`!Placeholder` sentinels are used, all of them must be filled at call time:
365+
If :data:`!Placeholder` sentinels are present, all of them must be filled at call time:
366366

367367
>>> from functools import partial, Placeholder
368368
>>> say_to_world = partial(print, Placeholder, Placeholder, "world!")

Doc/whatsnew/3.14.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,14 @@ Added support for converting any objects that have the
117117
:meth:`!as_integer_ratio` method to a :class:`~fractions.Fraction`.
118118
(Contributed by Serhiy Storchaka in :gh:`82017`.)
119119

120+
functools
121+
---------
122+
123+
* Added support to :func:`functools.partial` and
124+
:func:`functools.partialmethod` for :data:`functools.Placeholder` sentinels
125+
to reserve a place for positional arguments.
126+
(Contributed by Dominykas Grigonis in :gh:`119127`.)
127+
120128
json
121129
----
122130

@@ -133,14 +141,6 @@ operator
133141
to ``obj is not None``.
134142
(Contributed by Raymond Hettinger and Nico Mexis in :gh:`115808`.)
135143

136-
functools
137-
---------
138-
139-
* :func:`functools.partial` and :func:`functools.partialmethod` now support
140-
:data:`functools.Placeholder` sentinels to reserve a place for
141-
positional arguments.
142-
(Contributed by Dominykas Grigonis in :gh:`119127`)
143-
144144
os
145145
--
146146

Lib/functools.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,9 @@ def __reduce__(self):
301301
Placeholder = _PlaceholderType()
302302

303303
def _partial_prepare_merger(args):
304-
nargs = len(args)
305-
if not nargs:
304+
if not args:
306305
return 0, None
306+
nargs = len(args)
307307
order = []
308308
j = nargs
309309
for i, a in enumerate(args):
@@ -324,9 +324,9 @@ def _partial_new(cls, func, /, *args, **keywords):
324324
else:
325325
base_cls = partialmethod
326326
# func could be a descriptor like classmethod which isn't callable
327-
# assert issubclass(cls, partialmethod)
328327
if not callable(func) and not hasattr(func, "__get__"):
329-
raise TypeError(f"{func!r} is not callable or a descriptor")
328+
raise TypeError(f"the first argument {func!r} must be a callable "
329+
"or a descriptor")
330330
if args and args[-1] is Placeholder:
331331
raise TypeError("trailing Placeholders are not allowed")
332332
if isinstance(func, base_cls):
@@ -459,7 +459,7 @@ def _method(cls_or_self, /, *args, **keywords):
459459
args = args[phcount:]
460460
except IndexError:
461461
raise TypeError("missing positional arguments "
462-
"in 'partial' call; expected "
462+
"in 'partialmethod' call; expected "
463463
f"at least {phcount}, got {len(args)}")
464464
else:
465465
pto_args = self.args

Lib/inspect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2464,7 +2464,7 @@ def _signature_from_callable(obj, *,
24642464
assert (not sig_params or
24652465
first_wrapped_param is not sig_params[0])
24662466
# If there were placeholders set,
2467-
# first param is transformaed to positional only
2467+
# first param is transformed to positional only
24682468
if partialmethod.args.count(functools.Placeholder):
24692469
first_wrapped_param = first_wrapped_param.replace(
24702470
kind=Parameter.POSITIONAL_ONLY)

Lib/test/test_functools.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,19 @@ def test_setstate(self):
364364
f = self.partial(signature)
365365
f.__setstate__((capture, (PH, 1), dict(a=10), dict(attr=[])))
366366
self.assertEqual(signature(f), (capture, (PH, 1), dict(a=10), dict(attr=[])))
367-
with self.assertRaises(TypeError):
367+
with self.assertRaises(TypeError) as cm:
368368
self.assertEqual(f(), (PH, 1), dict(a=10))
369+
expected = ("missing positional arguments in 'partial' call; "
370+
"expected at least 1, got 0")
371+
self.assertEqual(cm.exception.args[0], expected)
369372
self.assertEqual(f(2), ((2, 1), dict(a=10)))
370373

371-
# Leading Placeholder error
374+
# Trailing Placeholder error
372375
f = self.partial(signature)
373-
with self.assertRaises(TypeError):
376+
with self.assertRaises(TypeError) as cm:
374377
f.__setstate__((capture, (1, PH), dict(a=10), dict(attr=[])))
378+
expected = "trailing Placeholders are not allowed"
379+
self.assertEqual(cm.exception.args[0], expected)
375380

376381
def test_setstate_errors(self):
377382
f = self.partial(signature)
@@ -556,10 +561,16 @@ class TestPartialPySubclass(TestPartialPy):
556561
partial = PyPartialSubclass
557562

558563
def test_subclass_optimization(self):
564+
# `partial` input to `partial` subclass
559565
p = py_functools.partial(min, 2)
560566
p2 = self.partial(p, 1)
561567
self.assertIs(p2.func, min)
562568
self.assertEqual(p2(0), 0)
569+
# `partial` subclass input to `partial` subclass
570+
p = self.partial(min, 2)
571+
p2 = self.partial(p, 1)
572+
self.assertIs(p2.func, min)
573+
self.assertEqual(p2(0), 0)
563574

564575

565576
class TestPartialMethod(unittest.TestCase):
@@ -702,10 +713,16 @@ def f(a, b, /):
702713
def test_subclass_optimization(self):
703714
class PartialMethodSubclass(functools.partialmethod):
704715
pass
716+
# `partialmethod` input to `partialmethod` subclass
705717
p = functools.partialmethod(min, 2)
706718
p2 = PartialMethodSubclass(p, 1)
707719
self.assertIs(p2.func, min)
708720
self.assertEqual(p2.__get__(0)(), 0)
721+
# `partialmethod` subclass input to `partialmethod` subclass
722+
p = PartialMethodSubclass(min, 2)
723+
p2 = PartialMethodSubclass(p, 1)
724+
self.assertIs(p2.func, min)
725+
self.assertEqual(p2.__get__(0)(), 0)
709726

710727

711728
class TestUpdateWrapper(unittest.TestCase):

0 commit comments

Comments
 (0)