Skip to content

Commit

Permalink
Merge branch 'main' into anno-to-source
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra authored Sep 25, 2024
2 parents 4921cb2 + 0268b07 commit 304e4fc
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 33 deletions.
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/crash.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ body:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "CPython main branch"
validations:
required: true
Expand Down
85 changes: 54 additions & 31 deletions Doc/tutorial/controlflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,52 @@ arguments. In chapter :ref:`tut-structures`, we will discuss in more detail abo

.. _tut-break:

:keyword:`!break` and :keyword:`!continue` Statements, and :keyword:`!else` Clauses on Loops
============================================================================================
:keyword:`!break` and :keyword:`!continue` Statements
=====================================================

The :keyword:`break` statement breaks out of the innermost enclosing
:keyword:`for` or :keyword:`while` loop.
:keyword:`for` or :keyword:`while` loop::

A :keyword:`!for` or :keyword:`!while` loop can include an :keyword:`!else` clause.
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(f"{n} equals {x} * {n//x}")
... break
...
4 equals 2 * 2
6 equals 2 * 3
8 equals 2 * 4
9 equals 3 * 3

The :keyword:`continue` statement continues with the next
iteration of the loop::

>>> for num in range(2, 10):
... if num % 2 == 0:
... print(f"Found an even number {num}")
... continue
... print(f"Found an odd number {num}")
...
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9

.. _tut-for-else:

:keyword:`!else` Clauses on Loops
=================================

In a :keyword:`!for` or :keyword:`!while` loop the :keyword:`!break` statement
may be paired with an :keyword:`!else` clause. If the loop finishes without
executing the :keyword:`!break`, the :keyword:`!else` clause executes.

In a :keyword:`for` loop, the :keyword:`!else` clause is executed
after the loop reaches its final iteration.
after the loop finishes its final iteration, that is, if no break occurred.

In a :keyword:`while` loop, it's executed after the loop's condition becomes false.

Expand Down Expand Up @@ -198,32 +234,19 @@ which searches for prime numbers::
9 equals 3 * 3

(Yes, this is the correct code. Look closely: the ``else`` clause belongs to
the :keyword:`for` loop, **not** the :keyword:`if` statement.)

When used with a loop, the ``else`` clause has more in common with the
``else`` clause of a :keyword:`try` statement than it does with that of
:keyword:`if` statements: a :keyword:`try` statement's ``else`` clause runs
when no exception occurs, and a loop's ``else`` clause runs when no ``break``
occurs. For more on the :keyword:`!try` statement and exceptions, see
:ref:`tut-handling`.

The :keyword:`continue` statement, also borrowed from C, continues with the next
iteration of the loop::

>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found an odd number", num)
...
Found an even number 2
Found an odd number 3
Found an even number 4
Found an odd number 5
Found an even number 6
Found an odd number 7
Found an even number 8
Found an odd number 9
the ``for`` loop, **not** the ``if`` statement.)

One way to think of the else clause is to imagine it paired with the ``if``
inside the loop. As the loop executes, it will run a sequence like
if/if/if/else. The ``if`` is inside the loop, encountered a number of times. If
the condition is ever true, a ``break`` will happen. If the condition is never
true, the ``else`` clause outside the loop will execute.

When used with a loop, the ``else`` clause has more in common with the ``else``
clause of a :keyword:`try` statement than it does with that of ``if``
statements: a ``try`` statement's ``else`` clause runs when no exception
occurs, and a loop's ``else`` clause runs when no ``break`` occurs. For more on
the ``try`` statement and exceptions, see :ref:`tut-handling`.

.. _tut-pass:

Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_type_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -1452,3 +1452,14 @@ def f[T: (int, str)](): pass
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.VALUE), (int, str))
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.FORWARDREF), (int, str))
self.assertEqual(annotationlib.call_evaluate_function(case.evaluate_constraints, annotationlib.Format.SOURCE), '(int, str)')

def test_const_evaluator(self):
T = TypeVar("T", bound=int)
self.assertEqual(repr(T.evaluate_bound), "<constevaluator <class 'int'>>")

ConstEvaluator = type(T.evaluate_bound)

with self.assertRaisesRegex(TypeError, r"cannot create '_typing\._ConstEvaluator' instances"):
ConstEvaluator() # This used to segfault.
with self.assertRaisesRegex(TypeError, r"cannot set 'attribute' attribute of immutable type '_typing\._ConstEvaluator'"):
ConstEvaluator.attribute = 1
5 changes: 3 additions & 2 deletions Objects/typevarobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ constevaluator_clear(PyObject *self)
}

static PyObject *
constevaluator_repr(PyObject *self, PyObject *repr)
constevaluator_repr(PyObject *self)
{
PyObject *value = ((constevaluatorobject *)self)->value;
return PyUnicode_FromFormat("<constevaluator %R>", value);
Expand Down Expand Up @@ -242,7 +242,8 @@ static PyType_Slot constevaluator_slots[] = {
PyType_Spec constevaluator_spec = {
.name = "_typing._ConstEvaluator",
.basicsize = sizeof(constevaluatorobject),
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE,
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE
| Py_TPFLAGS_DISALLOW_INSTANTIATION,
.slots = constevaluator_slots,
};

Expand Down

0 comments on commit 304e4fc

Please sign in to comment.