Skip to content

Commit 1b6241a

Browse files
committed
Merge branch 'main' into fastmock
* main: pythongh-99540: Constant hash for _PyNone_Type to aid reproducibility (pythonGH-99541) pythongh-100039: enhance __signature__ to work with str and callables (pythonGH-100168) pythongh-99830: asyncio: Document returns of remove_{reader,writer} (python#100302) "Compound statement" docs: Fix with-statement step indexing (python#100286) pythonGH-90043: Handle NaNs in COMPARE_OP_FLOAT_JUMP (pythonGH-100278)
2 parents a03f843 + 432117c commit 1b6241a

13 files changed

+108
-32
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,8 @@ Watching file descriptors
932932

933933
.. method:: loop.remove_reader(fd)
934934

935-
Stop monitoring the *fd* file descriptor for read availability.
935+
Stop monitoring the *fd* file descriptor for read availability. Returns
936+
``True`` if *fd* was previously being monitored for reads.
936937

937938
.. method:: loop.add_writer(fd, callback, *args)
938939

@@ -945,7 +946,8 @@ Watching file descriptors
945946

946947
.. method:: loop.remove_writer(fd)
947948

948-
Stop monitoring the *fd* file descriptor for write availability.
949+
Stop monitoring the *fd* file descriptor for write availability. Returns
950+
``True`` if *fd* was previously being monitored for writes.
949951

950952
See also :ref:`Platform Support <asyncio-platform-support>` section
951953
for some limitations of these methods.

Doc/reference/compound_stmts.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
506506
method returns without an error, then :meth:`__exit__` will always be
507507
called. Thus, if an error occurs during the assignment to the target list,
508508
it will be treated the same as an error occurring within the suite would
509-
be. See step 6 below.
509+
be. See step 7 below.
510510

511511
#. The suite is executed.
512512

Lib/enum.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
685685
'member order does not match _order_:\n %r\n %r'
686686
% (enum_class._member_names_, _order_)
687687
)
688-
688+
#
689689
return enum_class
690690

691691
def __bool__(cls):
@@ -1083,6 +1083,13 @@ class Enum(metaclass=EnumType):
10831083
attributes -- see the documentation for details.
10841084
"""
10851085

1086+
@classmethod
1087+
def __signature__(cls):
1088+
if cls._member_names_:
1089+
return '(*values)'
1090+
else:
1091+
return '(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)'
1092+
10861093
def __new__(cls, value):
10871094
# all enum instances are actually created during class construction
10881095
# without calling this method; this method is called by the metaclass'

Lib/inspect.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2443,10 +2443,18 @@ def _signature_from_callable(obj, *,
24432443
pass
24442444
else:
24452445
if sig is not None:
2446+
# since __text_signature__ is not writable on classes, __signature__
2447+
# may contain text (or be a callable that returns text);
2448+
# if so, convert it
2449+
o_sig = sig
2450+
if not isinstance(sig, (Signature, str)) and callable(sig):
2451+
sig = sig()
2452+
if isinstance(sig, str):
2453+
sig = _signature_fromstr(sigcls, obj, sig)
24462454
if not isinstance(sig, Signature):
24472455
raise TypeError(
24482456
'unexpected object {!r} in __signature__ '
2449-
'attribute'.format(sig))
2457+
'attribute'.format(o_sig))
24502458
return sig
24512459

24522460
try:

Lib/test/test_enum.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4259,7 +4259,7 @@ class TestEnumTypeSubclassing(unittest.TestCase):
42594259
Help on class Color in module %s:
42604260
42614261
class Color(enum.Enum)
4262-
| Color(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
4262+
| Color(*values)
42634263
|
42644264
| Method resolution order:
42654265
| Color
@@ -4315,7 +4315,7 @@ class Color(enum.Enum)
43154315
Help on class Color in module %s:
43164316
43174317
class Color(enum.Enum)
4318-
| Color(value, names=None, *values, module=None, qualname=None, type=None, start=1)
4318+
| Color(*values)
43194319
|
43204320
| Method resolution order:
43214321
| Color
@@ -4462,6 +4462,27 @@ def test_inspect_classify_class_attrs(self):
44624462
if failed:
44634463
self.fail("result does not equal expected, see print above")
44644464

4465+
def test_inspect_signatures(self):
4466+
from inspect import signature, Signature, Parameter
4467+
self.assertEqual(
4468+
signature(Enum),
4469+
Signature([
4470+
Parameter('new_class_name', Parameter.POSITIONAL_ONLY),
4471+
Parameter('names', Parameter.POSITIONAL_OR_KEYWORD),
4472+
Parameter('module', Parameter.KEYWORD_ONLY, default=None),
4473+
Parameter('qualname', Parameter.KEYWORD_ONLY, default=None),
4474+
Parameter('type', Parameter.KEYWORD_ONLY, default=None),
4475+
Parameter('start', Parameter.KEYWORD_ONLY, default=1),
4476+
Parameter('boundary', Parameter.KEYWORD_ONLY, default=None),
4477+
]),
4478+
)
4479+
self.assertEqual(
4480+
signature(enum.FlagBoundary),
4481+
Signature([
4482+
Parameter('values', Parameter.VAR_POSITIONAL),
4483+
]),
4484+
)
4485+
44654486
def test_test_simple_enum(self):
44664487
@_simple_enum(Enum)
44674488
class SimpleColor:

Lib/test/test_inspect.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3614,6 +3614,38 @@ def foo(): pass
36143614
self.assertEqual(signature_func(foo), inspect.Signature())
36153615
self.assertEqual(inspect.get_annotations(foo), {})
36163616

3617+
def test_signature_as_str(self):
3618+
self.maxDiff = None
3619+
class S:
3620+
__signature__ = '(a, b=2)'
3621+
3622+
self.assertEqual(self.signature(S),
3623+
((('a', ..., ..., 'positional_or_keyword'),
3624+
('b', 2, ..., 'positional_or_keyword')),
3625+
...))
3626+
3627+
def test_signature_as_callable(self):
3628+
# __signature__ should be either a staticmethod or a bound classmethod
3629+
class S:
3630+
@classmethod
3631+
def __signature__(cls):
3632+
return '(a, b=2)'
3633+
3634+
self.assertEqual(self.signature(S),
3635+
((('a', ..., ..., 'positional_or_keyword'),
3636+
('b', 2, ..., 'positional_or_keyword')),
3637+
...))
3638+
3639+
class S:
3640+
@staticmethod
3641+
def __signature__():
3642+
return '(a, b=2)'
3643+
3644+
self.assertEqual(self.signature(S),
3645+
((('a', ..., ..., 'positional_or_keyword'),
3646+
('b', 2, ..., 'positional_or_keyword')),
3647+
...))
3648+
36173649

36183650
class TestParameterObject(unittest.TestCase):
36193651
def test_signature_parameter_kinds(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
``None`` now hashes to a constant value. This is not a requirements change.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Handle NaNs when specializing :opcode:`COMPARE_OP` for :class:`float`
2+
values.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve signatures for enums and flags.

Objects/object.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1641,6 +1641,11 @@ none_bool(PyObject *v)
16411641
return 0;
16421642
}
16431643

1644+
static Py_hash_t none_hash(PyObject *v)
1645+
{
1646+
return 0xFCA86420;
1647+
}
1648+
16441649
static PyNumberMethods none_as_number = {
16451650
0, /* nb_add */
16461651
0, /* nb_subtract */
@@ -1692,7 +1697,7 @@ PyTypeObject _PyNone_Type = {
16921697
&none_as_number, /*tp_as_number*/
16931698
0, /*tp_as_sequence*/
16941699
0, /*tp_as_mapping*/
1695-
0, /*tp_hash */
1700+
(hashfunc)none_hash,/*tp_hash */
16961701
0, /*tp_call */
16971702
0, /*tp_str */
16981703
0, /*tp_getattro */

Python/bytecodes.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,13 +2024,11 @@ dummy_func(
20242024
// Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false)
20252025
DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
20262026
DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
2027+
STAT_INC(COMPARE_OP, hit);
20272028
double dleft = PyFloat_AS_DOUBLE(left);
20282029
double dright = PyFloat_AS_DOUBLE(right);
2029-
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
2030-
int sign_ish = 2*(dleft > dright) + 2 - (dleft < dright);
2031-
DEOPT_IF(isnan(dleft), COMPARE_OP);
2032-
DEOPT_IF(isnan(dright), COMPARE_OP);
2033-
STAT_INC(COMPARE_OP, hit);
2030+
// 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask
2031+
int sign_ish = 1 << (2 * (dleft >= dright) + (dleft <= dright));
20342032
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
20352033
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
20362034
jump = sign_ish & when_to_jump_mask;
@@ -2057,8 +2055,8 @@ dummy_func(
20572055
assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
20582056
Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
20592057
Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
2060-
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
2061-
int sign_ish = 2*(ileft > iright) + 2 - (ileft < iright);
2058+
// 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask
2059+
int sign_ish = 1 << (2 * (ileft >= iright) + (ileft <= iright));
20622060
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
20632061
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
20642062
jump = sign_ish & when_to_jump_mask;

Python/generated_cases.c.h

Lines changed: 5 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,15 +1951,16 @@ compare_op_fail_kind(PyObject *lhs, PyObject *rhs)
19511951

19521952

19531953
static int compare_masks[] = {
1954-
// 1-bit: jump if less than
1955-
// 2-bit: jump if equal
1954+
// 1-bit: jump if unordered
1955+
// 2-bit: jump if less
19561956
// 4-bit: jump if greater
1957-
[Py_LT] = 1 | 0 | 0,
1958-
[Py_LE] = 1 | 2 | 0,
1959-
[Py_EQ] = 0 | 2 | 0,
1960-
[Py_NE] = 1 | 0 | 4,
1961-
[Py_GT] = 0 | 0 | 4,
1962-
[Py_GE] = 0 | 2 | 4,
1957+
// 8-bit: jump if equal
1958+
[Py_LT] = 0 | 2 | 0 | 0,
1959+
[Py_LE] = 0 | 2 | 0 | 8,
1960+
[Py_EQ] = 0 | 0 | 0 | 8,
1961+
[Py_NE] = 1 | 2 | 4 | 0,
1962+
[Py_GT] = 0 | 0 | 4 | 0,
1963+
[Py_GE] = 0 | 0 | 4 | 8,
19631964
};
19641965

19651966
void
@@ -1980,7 +1981,7 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
19801981
assert(oparg <= Py_GE);
19811982
int when_to_jump_mask = compare_masks[oparg];
19821983
if (next_opcode == POP_JUMP_IF_FALSE) {
1983-
when_to_jump_mask = (1 | 2 | 4) & ~when_to_jump_mask;
1984+
when_to_jump_mask = (1 | 2 | 4 | 8) & ~when_to_jump_mask;
19841985
}
19851986
if (Py_TYPE(lhs) != Py_TYPE(rhs)) {
19861987
SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs));
@@ -2009,7 +2010,7 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
20092010
}
20102011
else {
20112012
_py_set_opcode(instr, COMPARE_OP_STR_JUMP);
2012-
cache->mask = (when_to_jump_mask & 2) == 0;
2013+
cache->mask = (when_to_jump_mask & 8) == 0;
20132014
goto success;
20142015
}
20152016
}

0 commit comments

Comments
 (0)