Boilerplate is of course detestable, but PyGame is already pretty low in boilerplate. https://github.com/kragen/pyconar-talk/blob/master/helloneg1... is five lines of code plus the shebang:

#!/usr/bin/python
from pygame import *
pantalla = display.set_mode((0, 0), FULLSCREEN)
draw.rect(pantalla, 128, (50, 100, 200, 400))
display.flip()
time.delay(2000)

Admittedly this is rather daunting compared to

print("hello, world")

but the benefits of the extra code are rather more immediate than those of public static void main: you can control whether your window is fullscreen or not, what size it is, when the screen updates, and how long the program runs before exiting.

When I see things like this,

> Your alien should now be appearing on screen! By passing the string 'alien' to the Actor class, it automatically loads the sprite,

I shudder. (That's in https://pygame-zero.readthedocs.io/en/stable/introduction.ht...) When you write a program and it doesn't load your sprite, how do you figure out why not? Maybe you called your sprite file images/alien.PNG or Images/alien.png or images/alien.jpeg or ./alien.png, and Pygame Zero doesn't happen to look in those places? I think you're going to have to resort to either trying things at random or using strace. Similarly, how hard is it to debug Pygame Zero displaying your window at the wrong size if you write HIEGHT = 300 instead of HEIGHT = 300?

In that talk I did eventually get around to drawing sprites, but it wasn't until one of the later examples, https://github.com/kragen/pyconar-talk/blob/master/reloj.py. The relevant code is

imagen = image.load('trashcan_empty.png')
...
pantalla.blit(imagen, (xx, yy))

(The talk was for a Spanish-speaking audience; "imagen" is Spanish for "image", and "pantalla" for "screen".) So that's what you're saving yourself when you say

alien = Actor('alien')
alien.pos = 100, 56
...
alien.draw()

But I don't have a lot of experience teaching kids to code, so maybe my intuitions about what makes things harder and what makes them easier are not well-founded. I'd be interested to hear other people's experiences of the kinds of errors beginners make and what kinds of interface design work best for them.

With Yeso, things are slightly more complicated than with PyGame; https://gitlab.com/kragen/bubbleos/blob/master/yeso/yv.py similarly opens a window and displays a PNG in it:

import sys

import yeso

if __name__ == '__main__':
image = yeso.read_png(sys.argv[1])
if image:
with yeso.Window(sys.argv[1], image.size) as w:
with w.frame() as f:
f.copy(image)
while True:
w.wait()

However, I'd argue that this interface is somewhat less error-prone than PyGame's; a common error I've had when starting new PyGame programs is forgetting to call pygame.display.flip(), which has the delightful symptom that whatever I've drawn just doesn't appear on the screen. By using a context manager, the Yeso Python binding avoids that error. As the comments point out, you can reduce yv.py to the following six lines of code if you depend on CPython's prompt finalization to flip buffers:

#!/usr/bin/python
import yeso, sys
_, n = sys.argv
p = yeso.read_png(n)
w = yeso.Window(n, p.size)
w.frame().copy(p)
while True: w.wait()

Yeso is still in its early stages, and I might change this interface a little bit, but I don't see why things need to be any more complicated than this for simple GUI programs. And my intuition is that making the interface less explicit makes it worse, for experienced programmers.

Proce55ing and Arduino offer at least some evidence that at least the setup()/draw()/mousemove() scaffolding is a significant help to beginners; they can get a moving object on the screen or a flashing LED before they have to know what a loop is, and the default behavior you have in Python or C of closing the window (for GUI programs) or restarting the firmware (for embedded programs) is not a good default.