One level of abstraction
Inside a function, every line should sit at the same level of detail. A function that calls charge(card) and then slices a postcode by hand mixes the plan with its details, and the reader has to keep changing zoom.
Write the top function as the steps of the plan, each one a call to a well-named helper, and push the details down into those helpers. Read from the top, the file then goes from overview to detail.
def make_slug(title):
words = clean_words(title)
return "-".join(words)
def clean_words(title):
words = title.lower().split()
return [word for word in words
if word.isalnum()]
print(make_slug("Clean Code in Python"))make_slug says what happens and clean_words says how. Python looks a helper up when the caller runs, so helpers can sit below the function that uses them.
If a step needs a comment to explain it, it is usually a helper waiting for a name.
Your task
Rewrite signup(form) as three calls: clean_email(text) (stripped, lower case, ValueError unless there is text on both sides of an @), clean_name(text) (capitalized words, ValueError if empty) and greeting(name). signup itself must not call string methods, and it returns the same dict as before.
The explanation opens here once every check passes.