Add "end" as a keyword for marking end of blocks

Not as a keyword; that would break all kinds of places where start and end are used as variables. It’s already been suggested that (assuming you aren’t using it in any other such way) you can assign a value to end and then use that. But you’re never going to get away from the fundamental that a code block needs to be indented, which means there needs to be something in there.

Of COURSE these are all workarounds. You’re asking for a fairly fundamental feature of Python to be changed. That means you’re going to get one of two things: either a workaround within the regular Python interpreter, or a different interpreter with different parsing rules (see eg Brython). You won’t get anything else. Inventing fundamentally different ways that things MIGHT be done is asking for a very different thing than the Python interpreter will ever be.

2 Likes

I’ve been playing with cpython’s source code, and yep, modifying the grammar is not an option. Then I resorted to the `Python/bltinmodule.c`, and simply added:

SETBUILTIN("end",                   Py_Ellipsis);

Just under:

SETBUILTIN("Ellipsis",              Py_Ellipsis);

And it works!

$ ./python

Python 3.15.0a8+ (heads/main-dirty:5fcab14c350, May  7 2026, 10:44:53) [GCC 15.2.1 20260209] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> end
Ellipsis

The patch is as follows:

--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -3533,6 +3533,7 @@ _PyBuiltin_Init(PyInterpreterState *interp)
 
     SETBUILTIN("None",                  Py_None);
     SETBUILTIN("Ellipsis",              Py_Ellipsis);
+    SETBUILTIN("end",                   Py_Ellipsis);
     SETBUILTIN("NotImplemented",        Py_NotImplemented);
     SETBUILTIN("False",                 Py_False);
     SETBUILTIN("True",                  Py_True);

Before rushing to your keyboards, calm down. I won’t make a PR of this. Discourage and unpopularity have already been sufficiently proven.

Who said anything about this?

I’m sure I haven’t said anything about indention removal.

If you mean that a block has at least to include `pass`, yep, that’s true, but I don’t see the relation to the addition of `end` as block’s finish mark.

No, that’s not true. As discussed above, adding a constant is not a fundamental change. Also, the use of this constant is totally optional, like in my modified `cpython`, I can write:

def foo(x):
    return abs(x) * 2

As well as:

def foo(x):
    return abs(x) * 2
end

(BTW, in my blasphemous python, `end` is correctly coloured).

Nothing is fundamentally changed. One can argue about it not fitting in the programming language’s style, meaningful indentation blah, blah, and so on and so forth. But that’s it.

It’s much easier than that.

>>> import builtins
>>> builtins.end = ...

Done! Stick those lines into your site.py or sitecustomize.py.

If you want end to have any actual meaning, you need to be able to omit the indented block:

And that’s a change, a fundamental change. If you DON’T have that, what’s the point? All you do is stick a word in there that has no meaning, no value. You can do it if you like, but why?

Not that I support the idea (I’m neutral), but technically it can be done as a soft keyword:

while True:
    ...
end while

for _ in ():
   ...
end for

if True:
   ...
else False:
   ...
end if

def f():
   ...
end def

Done! Stick those lines into your site.py or sitecustomize.py.

That’s an interesting option as well. I started to look at that module, but didn’t completely grasp its workings.

And that’s a change, a fundamental change. If you DON’T have that, what’s the point? All you do is stick a word in there that has no meaning, no value. You can do it if you like, but why?

As I’ve discussed at the beginning of this thread, as a visual mark. If you recall my first post, the objective is to improve readability.

God forbid that happens…

Interesting. I will try to find out more about this.

Look, if you want to improve readability, you have been given multiple ways of doing so without changing the language. “Readability” is a subjective question, so you’re free to use any of the suggested approaches in your own code, and no-one here will stop you.

But you keep insisting that these solutions aren’t “good enough” or are “workarounds”. I can’t see how to interpret that except as meaning that there’s some other motive that you’re not explaining clearly. Maybe you think your idea of readability is the correct view, and therefore that other people should be encouraged to follow your approach? Again, if they want to, sure - they have all the existing methods available, just like you do. But you won’t find support here for forcing others to follow your preference.

Or do you want some sort of “official” approval for your approach? It seems pretty clear by this point that you’re not going to get that - the core devs and experienced Python users here have shown no interest in this as a recommended approach, much less a language feature. I think at this point, you need to accept that, and move on.

I’d really encourage you not to bother. You’re not going to get support for changing the language over this. So the only possible reason[1] for finding out how to do so would be if you plan on creating and maintaining your own fork of Python. And while you’re welcome to do that, it’s a lot of work and probably not as rewarding as you’d hope it will be.


  1. Apart from intellectual curiosity, of course, which is always a good reason :slightly_smiling_face: ↩︎

7 Likes

This is one of a class of threads where I wonder “Why is this still going? I thought the best possible answers had already been given?” Usually it indicates that there’s some kind of communication failure.
I’m not sure I can bridge the gap here, but will give it a try.

When I look at the justifying use-case from the original post, I see something which might explain the feeling that “something is wrong”.

In a context like this, that “dangling” i += 1 is, indeed, a sign of a problem. The problem is not, however, the absence of an end-block delimiter. The problem is that work that should be structured into an iterator has been done manually.

Here’s some code which walks the elements of a list, non-idiomatically:

i = 0
while i < len(mylist):
    print(f"mylist[{i}]:", mylist[i])
    i += 1

And here’s a more idiomatic version:

for i, item in enumerate(mylist):
    print(f"mylist[{i}]:", item)

In a larger block with more complexity, the difference between manually managing iteration and delegating that work to a clean abstraction, like the enumerate() builtin, becomes much more significant.

The relevant abstraction is generally going to be iterators for loops. There are some other obvious cases, like context managers for try-finally and function decomposition for deeply indented suites. I would say that any case in which the absence of an “end” marker negatively impacts code readability, to any significant degree, indicates an improper abstraction.

So what is the proper abstraction for…

i = 0
while < 100:
    if i % 2 == 0:
        i *= 2
        print(i)

        if ... # more conditions
            do_something_else()

    i += 1

?

Definitely an iterator. You have several options for how to phrase it, but just to show one:

def my_conditional_walk(maximum):
    i = 0
    while i < maximum:
        if i % 2 == 0:
            i *= 2
            yield i
        i += 1


for i in my_conditional_walk(100):
    print(i)

There are dozens of ways to do it, but my point is that I strongly agree with the instinct that reading a large block of code, followed by i += 1 is messy and hard to read. Even C has for-loops these days!

The solution is not to start trying to change to language to put a band-aid on it – with a large enough body for the loop, I don’t even think an end marker would help improve this. Instead, find an abstraction which better separates looping/iteration from the rest of your logic.

1 Like

Oh, here we go…

Maybe you think your idea of readability is the correct view, and therefore that other people should be encouraged to follow your approach?

Nope. From the very beginning, I stated that the feature, should it be added to the language, should be optional.

@StephenRosen
This is one of a class of threads where I wonder “Why is this still going? I thought the best possible answers had already been given?” Usually it indicates that there’s some kind of communication failure.

I guess the thread will go on while there’s still people interested on the topic. The thread relived when @ABaumstumpf said it could be interesting for tooling, but no one has taken the effort to answer.

In a context like this, that “dangling” i += 1 is, indeed, a sign of a problem. The problem is not, however, the absence of an end-block delimiter. The problem is that work that should be structured into an iterator has been done manually.

That was just an example, i.e., a small instance that everyone can understand in a simple look, but still illustrates the point. Sure, it can be transformed to avoid the problem, in a dozen ways, because it’s a so trivial piece of code.

Perhaps you need a weathervane to show you which way the wind is blowing, but it ought to be clear that this feature is NOT going to be added to the language. You can say “should it be added” as much as you like, but it won’t be. So you are going to have to figure out how to live your life with that fact. You have several good options available, which have already been laid out in this thread, but changes to the language [1] aren’t among them.


  1. other than your own fork of it, that’s one of the options you have ↩︎

3 Likes

Let me ask a procedural question to the people keeping this thread alive.

What, exactly, would it take for you to accept the response that the requested feature will not be added to the language, and let the topic drop once and for all? Remember that the “Ideas” category is for considering proposals to change the Python language, and one possible outcome has to be that the proposal is rejected. So we need to know how to make it clear when that happens, as in a case like this.

3 Likes

Given the extreme rudeness of last posts you can count on me not participating in this or in any other thread.

Obviously, I cannot talk for others, though.

I’ll apologise right now for anything in my posts you felt was rude. That was never my intention.

However, I genuinely don’t know what rudeness you are referring to. I’m trying to understand what you want from this thread, as it seems you’re not getting the feedback you want. If you feel I did so in a rude way, please let me know what I said that felt rude, and I’ll fix that in future. But my apology stands even if you don’t want to reply further to me.

4 Likes

I desire to associate myself with that expression of regret. It was likewise never my intention to be rude, but to offer the best viable alternatives.

2 Likes

I can see how several responses, including not recently, could be perceived as rude. I think their intention was to be blunt, to make it very clear the idea was rejected. Subtler ways of saying the proposal had no path forward did not appear to be effective, so a more direct approach was warranted.

6 Likes

This isn’t going anywhere productive.