Mehr Happy Cyber-Chris noises

Dies ist eine Fortsetzung der Aufgabe “Happy Cyber-Chris noises”.

Story

Sie arbeiten seit einer ganzen Weile für eine Sicherheitsfirma an einer Codebase, für Python-code haben Sie nie Typehints verwendet. Eines Tages stirbt ihr lockerer Chef bei einem Unterwasserabenteuer. Und ausgerechnet Cyber-Chris nimmt seinen Platz ein! Ihnen ist sofort bewusst: Bearbeiten Sie Ihren Python-code nicht sofort, werden sie gefeuert!

Aufgabe

Nun geht es ans eingemachte! Ein Fehler könnten Ihnen ihren Job kosten!

Bearbeiten Sie den Code so, dass via Typehinting jeder Variablen- und Rückgabetyp sofort ersichtlich wird!

# Fehlermeldungen für den Nutzer
message_1 = "Das Passwort ist zu kurz!"
message_2 = "Das Passwort muss min. 3 Buchstaben enthalten!"
message_3 = "Das Passwort muss min. 2 Zahlen enthalten!"
message_4 = "Das Passwort benötigt min. 1 Sonderzeichen!"

# Alle Zahlen, Buchstaben und Sonderzeichen auf der Welt hust hust
numbers = (3, 9, 4, 2)
characters = {"letters": ('W', 'h', 'e', 'a', 't', 'l', 'e', 'y'), "special": ('!', '?', '-', '@')}

# Diese Funktion nimmt ein Passwort, bestehend aus einem String und prüft, ob dieses Passwort sicher ist.
def naive_password_check(password):
    password_length = len(password)
    if(password_length < 8):
        print(message_1)
        return False

    if(character_counter(password, characters["letters"]) < 3):
        print(message_2)
        return False

    if(number_counter(password) < 2):
        print(message_3)
        return False

    if(character_counter(password, characters["special"]) < 1):
        print(message_4)
        return False

    return True


# Helferfunktionen
def character_counter(password, characters):
    i = 0
    for char in password:
        if char in characters:
            i = i + 1
    return i

def number_counter(password):
    i = 0
    for char in password:
        try:
            if int(char) in numbers:
                i = i + 1
        except:
            continue
    return i

def main():
    if(naive_password_check("Wheatley!42")):
        print("Stein starkes Passwort!")
    return 0

if __name__ == "__main__":
    main()

Bonus

Damit die Länge der Bonusaufgabe euch nicht erschreckt :P

Dem Python Interpreter sind typehints völlig Schnuppe. Der Interpreter hätte Sie z.B. in Aufgabe 2 nicht davon abgehalten, naive_password_check(123) auszuführen, obwohl 123 ein Integer und kein String ist. Der Python Interpreter hätte auch nicht gemeckert, wenn naive_password_check was anderes als ein Boolean zurückgegeben hätte. Natürlich stößt der Interpreter beim interpretieren dann ggf. trotzdem auf Probleme, da die Typen nicht kompatibel sind. Probieren Sie es gerne selbst aus!

Abhilfe schafft hier das Programm mypy. mypy ist ein sogennanter Typechecker. Typechecker überprüfen, ob man richtig mit Variablen und Funktionen beüzglich den Typen umgeht. Ein typecheck mit mypy bezüglich diesem Python-code würde z.B. diesen Fehler aufrufen:

def stringify(num: int) -> str:
    return num

#test2.py:2: error: Incompatible return value type (got "int", expected "str")  [return-value]

Es ist hier wichtig anzumerken, dass mypy Variablen und Funktionen logischerweise nur dann typechecken kann, wenn man ihre jeweiligen Typen getypehinted hat. mypy wird Variablen und Funktionen ohne type-hint sogar überspringen!

Hier wird mypy z.B. nicht meckern, da es main überspringt:

def main():
    naive_password_check(9000)
    return 0

So würde es aber klappen:

def main()->int:
    naive_password_check(9999)
    return 0

# test.py:59: error: Argument 1 to "naive_password_check" has incompatible type "int"; expected "str"  [arg-type]

Möchte man, dass mypy alles trotzdem checkt, so kann man die Option --strict hinzügen, dass heißt man tippt dann mypy FILE --strict in die Konsole ein (siehe Installation unten). mypy wird dann meckern, wenn man vergessen hat etwas zu typehinten.

Nun zu der Aufgabe:

Überprüfen Sie mit dem Programm mypy selbstständig, ob Ihre Typehints sinnvoll sind.

Installation und Nutzung von mypy:
  • Auf der VM bzw. auf den meisten Linux distros geht es ganz einfach:

    1. Terminal öffnen
    2. sudo apt install mypy in die Konsole eingeben und Passwort eingeben. Falls nicht auf der VM und apt nicht Packetmanager der jeweiligen Distro, einfach pip oder ggf. anderen Packetmanger nutzen. Auf Debian/Ubuntu/Mint müsst es klappen.
    3. Mit mypy FILE [OPTIONEN] kann man im Terminal sein Code checken. Eine Datei test.py welche sich auf dem Desktop befindet, würde man z.B. mit mypy ~/Desktop/test.py checken (~ ist ein Alias für den Pfad zum home-directory, tippen Sie echo ~ in die Konsole ein um zu sehen wie der Pfad ausgeschrieben aussehen würde).
  • Sonst:

    1. Terminal/CMD öffnen
    2. Mit pip sollte man mypy instalieren können: pip install mypy eingeben in Terminal/CMD. Dazu muss pip auf ihrem Rechner installiert sein.
    3. Siehe 3. oben, der Pfad muss allerdings bei Windows und vllt. auch bei Mac angepasst werden (weiß nicht ob ~ auf Mac klappt)
    4. Fall dies nicht funktioniert hat, selbst recherchieren oder Tutor fragen. Oder einfach die VM nutzen und sich im Vorkurs, die AlPro und im Praktikum OOSE ganz viel Arbeit sparen! :)

Tipp

Achtung Spoiler!
# So geben Sie den Type einer Variable an
var_1: int = 1
var_2: float = 1.0
var_4: bool = True
var_5: str = "test"

# Für Listen und Dictionaries macht man das so
var_6: list[int] = [1]
var_7: dict[str, float] = {"field": 2.0}

# Die Parametertypen und Rückgabetyp einer Funktion gibt man so an
def stringify(num: int) -> str:
    return str(num)

Weitere Informationen unter https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html