The CGI module is deprecated starting in Python 3.11 and will be removed in Python 3.13 (since "spinning up a new Python instance for every single request" is getting less feasible as time goes on; even Moore's law can't keep up with the code swell...)
If you want to take the "HTTP POST where the choice of submit button matters" route in the future, here's how you might do it in WSGI:
<!-- client side (no JS required!) -->
<form action="/cgi-bin/do.py" method="POST">
<input name="username" placeholder="username" required/>
<button type="submit" value="login" name="submitter">Log In</button>
<button type="submit" value="register" name="submitter">Register New User</button>
<button type="submit" value="reset" name="submitter">Reset Password</button>
</form>
# server side (no pypi packages required!)
# eschews cgi library, for Python 3.11+
import urllib.parse
def main_wsgi(environ, start_response):
if environ['PATH_INFO'] == '/cgi-bin/do.py':
assert environ['REQUEST_METHOD'] == 'POST'
form = _parse_form(environ)
action = form.get('submitter')
username = form.get('username')
assert '<' not in username
start_response('200 OK', [('Content-Type', 'text/html')])
yield '<h1>'.encode()
match action:
case 'login':
yield f'Logged in as {username}!'.encode()
case 'register':
yield f'"{username}" registered!'.encode()
case 'reset':
yield f'Password reset. Check your e-mail, {username}!'.encode()
case _:
yield f'400 Nice Try'.encode()
yield '</h1>'.encode()
else:
assert environ['REQUEST_METHOD'] == 'GET'
start_response('200 OK', [('Content-Type', 'text/html')])
yield '<form action="/cgi-bin/do.py" method="POST">'.encode()
yield '<input name="username" placeholder="username" required />'.encode()
yield '<button type="submit" value="login" name="submitter">Log In</button>'.encode()
yield '<button type="submit" value="register" name="submitter">Register New User</button>'.encode()
yield '<button type="submit" value="reset" name="submitter">Reset Password</button>'.encode()
yield '</form>'.encode()
def _parse_form(environ, /):
assert environ['CONTENT_TYPE'] == 'application/x-www-form-urlencoded', 'NotImplementedError; TODO\nhttps://andrew-d.github.io/python-multipart'
l = int(environ['CONTENT_LENGTH'])
body = environ['wsgi.input'].read(l)
body = body.decode('ascii', errors='surrogateescape')
return dict(urllib.parse.parse_qsl(body))
if __name__ == '__main__':
import wsgiref.simple_server
wsgiref.simple_server.make_server('', 8000, main_wsgi).serve_forever()