Skip to content

Commit

Permalink
Merge branch 'main' into fastmock
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
carljm committed Dec 16, 2022
2 parents a03f843 + 432117c commit 1b6241a
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 32 deletions.
6 changes: 4 additions & 2 deletions Doc/library/asyncio-eventloop.rst
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,8 @@ Watching file descriptors

.. method:: loop.remove_reader(fd)

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

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

Expand All @@ -945,7 +946,8 @@ Watching file descriptors

.. method:: loop.remove_writer(fd)

Stop monitoring the *fd* file descriptor for write availability.
Stop monitoring the *fd* file descriptor for write availability. Returns
``True`` if *fd* was previously being monitored for writes.

See also :ref:`Platform Support <asyncio-platform-support>` section
for some limitations of these methods.
Expand Down
2 changes: 1 addition & 1 deletion Doc/reference/compound_stmts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ The execution of the :keyword:`with` statement with one "item" proceeds as follo
method returns without an error, then :meth:`__exit__` will always be
called. Thus, if an error occurs during the assignment to the target list,
it will be treated the same as an error occurring within the suite would
be. See step 6 below.
be. See step 7 below.

#. The suite is executed.

Expand Down
9 changes: 8 additions & 1 deletion Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
'member order does not match _order_:\n %r\n %r'
% (enum_class._member_names_, _order_)
)

#
return enum_class

def __bool__(cls):
Expand Down Expand Up @@ -1083,6 +1083,13 @@ class Enum(metaclass=EnumType):
attributes -- see the documentation for details.
"""

@classmethod
def __signature__(cls):
if cls._member_names_:
return '(*values)'
else:
return '(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)'

def __new__(cls, value):
# all enum instances are actually created during class construction
# without calling this method; this method is called by the metaclass'
Expand Down
10 changes: 9 additions & 1 deletion Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2443,10 +2443,18 @@ def _signature_from_callable(obj, *,
pass
else:
if sig is not None:
# since __text_signature__ is not writable on classes, __signature__
# may contain text (or be a callable that returns text);
# if so, convert it
o_sig = sig
if not isinstance(sig, (Signature, str)) and callable(sig):
sig = sig()
if isinstance(sig, str):
sig = _signature_fromstr(sigcls, obj, sig)
if not isinstance(sig, Signature):
raise TypeError(
'unexpected object {!r} in __signature__ '
'attribute'.format(sig))
'attribute'.format(o_sig))
return sig

try:
Expand Down
25 changes: 23 additions & 2 deletions Lib/test/test_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -4259,7 +4259,7 @@ class TestEnumTypeSubclassing(unittest.TestCase):
Help on class Color in module %s:
class Color(enum.Enum)
| Color(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
| Color(*values)
|
| Method resolution order:
| Color
Expand Down Expand Up @@ -4315,7 +4315,7 @@ class Color(enum.Enum)
Help on class Color in module %s:
class Color(enum.Enum)
| Color(value, names=None, *values, module=None, qualname=None, type=None, start=1)
| Color(*values)
|
| Method resolution order:
| Color
Expand Down Expand Up @@ -4462,6 +4462,27 @@ def test_inspect_classify_class_attrs(self):
if failed:
self.fail("result does not equal expected, see print above")

def test_inspect_signatures(self):
from inspect import signature, Signature, Parameter
self.assertEqual(
signature(Enum),
Signature([
Parameter('new_class_name', Parameter.POSITIONAL_ONLY),
Parameter('names', Parameter.POSITIONAL_OR_KEYWORD),
Parameter('module', Parameter.KEYWORD_ONLY, default=None),
Parameter('qualname', Parameter.KEYWORD_ONLY, default=None),
Parameter('type', Parameter.KEYWORD_ONLY, default=None),
Parameter('start', Parameter.KEYWORD_ONLY, default=1),
Parameter('boundary', Parameter.KEYWORD_ONLY, default=None),
]),
)
self.assertEqual(
signature(enum.FlagBoundary),
Signature([
Parameter('values', Parameter.VAR_POSITIONAL),
]),
)

def test_test_simple_enum(self):
@_simple_enum(Enum)
class SimpleColor:
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -3614,6 +3614,38 @@ def foo(): pass
self.assertEqual(signature_func(foo), inspect.Signature())
self.assertEqual(inspect.get_annotations(foo), {})

def test_signature_as_str(self):
self.maxDiff = None
class S:
__signature__ = '(a, b=2)'

self.assertEqual(self.signature(S),
((('a', ..., ..., 'positional_or_keyword'),
('b', 2, ..., 'positional_or_keyword')),
...))

def test_signature_as_callable(self):
# __signature__ should be either a staticmethod or a bound classmethod
class S:
@classmethod
def __signature__(cls):
return '(a, b=2)'

self.assertEqual(self.signature(S),
((('a', ..., ..., 'positional_or_keyword'),
('b', 2, ..., 'positional_or_keyword')),
...))

class S:
@staticmethod
def __signature__():
return '(a, b=2)'

self.assertEqual(self.signature(S),
((('a', ..., ..., 'positional_or_keyword'),
('b', 2, ..., 'positional_or_keyword')),
...))


class TestParameterObject(unittest.TestCase):
def test_signature_parameter_kinds(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``None`` now hashes to a constant value. This is not a requirements change.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Handle NaNs when specializing :opcode:`COMPARE_OP` for :class:`float`
values.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve signatures for enums and flags.
7 changes: 6 additions & 1 deletion Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,11 @@ none_bool(PyObject *v)
return 0;
}

static Py_hash_t none_hash(PyObject *v)
{
return 0xFCA86420;
}

static PyNumberMethods none_as_number = {
0, /* nb_add */
0, /* nb_subtract */
Expand Down Expand Up @@ -1692,7 +1697,7 @@ PyTypeObject _PyNone_Type = {
&none_as_number, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
(hashfunc)none_hash,/*tp_hash */
0, /*tp_call */
0, /*tp_str */
0, /*tp_getattro */
Expand Down
12 changes: 5 additions & 7 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2024,13 +2024,11 @@ dummy_func(
// Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false)
DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
STAT_INC(COMPARE_OP, hit);
double dleft = PyFloat_AS_DOUBLE(left);
double dright = PyFloat_AS_DOUBLE(right);
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
int sign_ish = 2*(dleft > dright) + 2 - (dleft < dright);
DEOPT_IF(isnan(dleft), COMPARE_OP);
DEOPT_IF(isnan(dright), COMPARE_OP);
STAT_INC(COMPARE_OP, hit);
// 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask
int sign_ish = 1 << (2 * (dleft >= dright) + (dleft <= dright));
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
jump = sign_ish & when_to_jump_mask;
Expand All @@ -2057,8 +2055,8 @@ dummy_func(
assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
int sign_ish = 2*(ileft > iright) + 2 - (ileft < iright);
// 2 if <, 4 if >, 8 if ==; this matches when_to_jump_mask
int sign_ish = 1 << (2 * (ileft >= iright) + (ileft <= iright));
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
jump = sign_ish & when_to_jump_mask;
Expand Down
12 changes: 5 additions & 7 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 11 additions & 10 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -1951,15 +1951,16 @@ compare_op_fail_kind(PyObject *lhs, PyObject *rhs)


static int compare_masks[] = {
// 1-bit: jump if less than
// 2-bit: jump if equal
// 1-bit: jump if unordered
// 2-bit: jump if less
// 4-bit: jump if greater
[Py_LT] = 1 | 0 | 0,
[Py_LE] = 1 | 2 | 0,
[Py_EQ] = 0 | 2 | 0,
[Py_NE] = 1 | 0 | 4,
[Py_GT] = 0 | 0 | 4,
[Py_GE] = 0 | 2 | 4,
// 8-bit: jump if equal
[Py_LT] = 0 | 2 | 0 | 0,
[Py_LE] = 0 | 2 | 0 | 8,
[Py_EQ] = 0 | 0 | 0 | 8,
[Py_NE] = 1 | 2 | 4 | 0,
[Py_GT] = 0 | 0 | 4 | 0,
[Py_GE] = 0 | 0 | 4 | 8,
};

void
Expand All @@ -1980,7 +1981,7 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
assert(oparg <= Py_GE);
int when_to_jump_mask = compare_masks[oparg];
if (next_opcode == POP_JUMP_IF_FALSE) {
when_to_jump_mask = (1 | 2 | 4) & ~when_to_jump_mask;
when_to_jump_mask = (1 | 2 | 4 | 8) & ~when_to_jump_mask;
}
if (Py_TYPE(lhs) != Py_TYPE(rhs)) {
SPECIALIZATION_FAIL(COMPARE_OP, compare_op_fail_kind(lhs, rhs));
Expand Down Expand Up @@ -2009,7 +2010,7 @@ _Py_Specialize_CompareOp(PyObject *lhs, PyObject *rhs, _Py_CODEUNIT *instr,
}
else {
_py_set_opcode(instr, COMPARE_OP_STR_JUMP);
cache->mask = (when_to_jump_mask & 2) == 0;
cache->mask = (when_to_jump_mask & 8) == 0;
goto success;
}
}
Expand Down

0 comments on commit 1b6241a

Please sign in to comment.