Η διαδρομή για τον έλεγχο πληκτρολόγησης 4 εκατομμυρίων γραμμών κώδικα Python. Μέρος 1

Σήμερα φέρνουμε στην προσοχή σας το πρώτο μέρος της μετάφρασης του υλικού σχετικά με το πώς το Dropbox αντιμετωπίζει τον έλεγχο τύπων του κώδικα Python.

Η διαδρομή για τον έλεγχο πληκτρολόγησης 4 εκατομμυρίων γραμμών κώδικα Python. Μέρος 1

Το Dropbox γράφει πολλά στην Python. Είναι μια γλώσσα που χρησιμοποιούμε εξαιρετικά ευρέως, τόσο για υπηρεσίες back-end όσο και για εφαρμογές πελάτη για επιτραπέζιους υπολογιστές. Χρησιμοποιούμε επίσης πολύ Go, TypeScript και Rust, αλλά η Python είναι η κύρια γλώσσα μας. Λαμβάνοντας υπόψη την κλίμακα μας, και μιλάμε για εκατομμύρια γραμμές κώδικα Python, αποδείχθηκε ότι η δυναμική πληκτρολόγηση ενός τέτοιου κώδικα περιέπλεξε άσκοπα την κατανόησή του και άρχισε να επηρεάζει σοβαρά την παραγωγικότητα της εργασίας. Για να μετριαστεί αυτό το πρόβλημα, αρχίσαμε να μεταφέρουμε σταδιακά τον κώδικά μας σε έλεγχο στατικού τύπου χρησιμοποιώντας το mypy. Αυτό είναι ίσως το πιο δημοφιλές αυτόνομο σύστημα ελέγχου τύπων για την Python. Το Mypy είναι ένα έργο ανοιχτού κώδικα, οι κύριοι προγραμματιστές του εργάζονται στο Dropbox.

Η Dropbox ήταν μια από τις πρώτες εταιρείες που εφάρμοσε τον έλεγχο στατικού τύπου σε κώδικα Python σε αυτή την κλίμακα. Το Mypy χρησιμοποιείται σε χιλιάδες έργα αυτές τις μέρες. Αυτό το εργαλείο αμέτρητες φορές, όπως λένε, «δοκιμάστηκε στη μάχη». Έχουμε κάνει πολύ δρόμο για να φτάσουμε εδώ που είμαστε τώρα. Στην πορεία, υπήρξαν πολλές ανεπιτυχείς επιχειρήσεις και αποτυχημένα πειράματα. Αυτή η ανάρτηση καλύπτει την ιστορία του στατικού ελέγχου τύπων στην Python, από τις δύσκολες αρχές του ως μέρος του ερευνητικού μου έργου, μέχρι σήμερα, όταν ο έλεγχος τύπων και η υπόδειξη τύπων έχουν γίνει συνηθισμένα για αμέτρητους προγραμματιστές που γράφουν σε Python. Αυτοί οι μηχανισμοί υποστηρίζονται πλέον από πολλά εργαλεία όπως IDE και αναλυτές κώδικα.

Διαβάστε το δεύτερο μέρος

Γιατί είναι απαραίτητος ο έλεγχος τύπου;

Εάν έχετε χρησιμοποιήσει ποτέ Python με δυναμική πληκτρολόγηση, μπορεί να έχετε κάποια σύγχυση ως προς το γιατί έχει γίνει τόση φασαρία γύρω από τη στατική πληκτρολόγηση και το mypy τον τελευταίο καιρό. Ή ίσως σας αρέσει η Python ακριβώς λόγω της δυναμικής της πληκτρολόγησης και αυτό που συμβαίνει απλά σας αναστατώνει. Το κλειδί για την αξία της στατικής πληκτρολόγησης είναι η κλίμακα των λύσεων: όσο μεγαλύτερο το έργο σας, τόσο περισσότερο κλίνετε προς τη στατική πληκτρολόγηση και στο τέλος, τόσο περισσότερο το χρειάζεστε πραγματικά.

Ας υποθέσουμε ότι ένα συγκεκριμένο έργο έχει φτάσει το μέγεθος δεκάδων χιλιάδων γραμμών και αποδείχθηκε ότι αρκετοί προγραμματιστές εργάζονται σε αυτό. Εξετάζοντας ένα παρόμοιο έργο, με βάση την εμπειρία μας, μπορούμε να πούμε ότι η κατανόηση του κώδικα του θα είναι το κλειδί για να παραμείνουν παραγωγικοί οι προγραμματιστές. Χωρίς σχολιασμούς τύπων, μπορεί να είναι δύσκολο να καταλάβουμε, για παράδειγμα, ποια ορίσματα πρέπει να περάσουν σε μια συνάρτηση ή ποιους τύπους μπορεί να επιστρέψει μια συνάρτηση. Ακολουθούν τυπικές ερωτήσεις που συχνά είναι δύσκολο να απαντηθούν χωρίς τη χρήση σχολιασμών τύπων:

  • Μπορεί αυτή η συνάρτηση να επιστρέψει None?
  • Ποιο θα πρέπει να είναι αυτό το επιχείρημα; items?
  • Ποιος είναι ο τύπος χαρακτηριστικού id: int είναι, str, ή μήπως κάποιος προσαρμοσμένος τύπος;
  • Πρέπει αυτό το επιχείρημα να είναι λίστα; Είναι δυνατόν να του περάσετε πλειάδα;

Εάν κοιτάξετε το ακόλουθο απόσπασμα κώδικα με σχολιασμό τύπου και προσπαθήσετε να απαντήσετε σε παρόμοιες ερωτήσεις, αποδεικνύεται ότι αυτή είναι η απλούστερη εργασία:

class Resource:
    id: bytes
    ...
    def read_metadata(self, 
                      items: Sequence[str]) -> Dict[str, MetadataItem]:
        ...

  • read_metadata δεν επιστρέφει None, αφού ο τύπος επιστροφής δεν είναι Optional[…].
  • Επιχείρημα items είναι μια ακολουθία γραμμών. Δεν μπορεί να επαναληφθεί τυχαία.
  • Χαρακτηριστικό id είναι μια σειρά από byte.

Σε έναν ιδανικό κόσμο, θα περίμενε κανείς ότι όλες αυτές οι λεπτές αποχρώσεις θα περιγράφονταν στην ενσωματωμένη τεκμηρίωση (docstring). Αλλά η εμπειρία δίνει πολλά παραδείγματα του γεγονότος ότι μια τέτοια τεκμηρίωση συχνά δεν παρατηρείται στον κώδικα με τον οποίο πρέπει να εργαστείτε. Ακόμα κι αν υπάρχει τέτοια τεκμηρίωση στον κώδικα, δεν μπορεί κανείς να υπολογίζει στην απόλυτη ορθότητά του. Αυτή η τεκμηρίωση μπορεί να είναι ασαφής, ανακριβής και επιδεκτική παρεξηγήσεων. Σε μεγάλες ομάδες ή μεγάλα έργα, αυτό το πρόβλημα μπορεί να γίνει εξαιρετικά οξύ.

Ενώ η Python υπερέχει στα αρχικά ή στα ενδιάμεσα στάδια των έργων, κάποια στιγμή επιτυχημένα έργα και εταιρείες που χρησιμοποιούν Python μπορεί να αντιμετωπίσουν το ζωτικής σημασίας ερώτημα: «Πρέπει να ξαναγράψουμε τα πάντα σε μια στατικά δακτυλογραφημένη γλώσσα;».

Τα συστήματα ελέγχου τύπων όπως το mypy επιλύουν το παραπάνω πρόβλημα παρέχοντας στον προγραμματιστή μια επίσημη γλώσσα για την περιγραφή τύπων και ελέγχοντας ότι οι δηλώσεις τύπων αντιστοιχούν στην υλοποίηση του προγράμματος (και, προαιρετικά, ελέγχοντας την ύπαρξή τους). Σε γενικές γραμμές, μπορούμε να πούμε ότι αυτά τα συστήματα θέτουν στη διάθεσή μας κάτι σαν προσεκτικά ελεγμένη τεκμηρίωση.

Η χρήση τέτοιων συστημάτων έχει άλλα πλεονεκτήματα και είναι ήδη εντελώς μη τετριμμένα:

  • Το σύστημα ελέγχου τύπου μπορεί να εντοπίσει κάποια μικρά (και όχι τόσο μικρά) σφάλματα. Χαρακτηριστικό παράδειγμα είναι όταν ξεχνούν να επεξεργαστούν μια τιμή None ή κάποια άλλη ειδική κατάσταση.
  • Η αναδιαμόρφωση κώδικα είναι πολύ απλοποιημένη επειδή το σύστημα ελέγχου τύπου είναι συχνά πολύ ακριβές σχετικά με τον κώδικα που πρέπει να αλλάξει. Ταυτόχρονα, δεν χρειάζεται να ελπίζουμε σε 100% κάλυψη κωδικών με δοκιμές, κάτι που, σε κάθε περίπτωση, συνήθως δεν είναι εφικτό. Δεν χρειάζεται να εμβαθύνουμε στα βάθη του ίχνους στοίβας για να μάθουμε την αιτία του προβλήματος.
  • Ακόμη και σε μεγάλα έργα, το mypy μπορεί συχνά να κάνει πλήρη έλεγχο τύπου σε κλάσματα του δευτερολέπτου. Και η εκτέλεση των δοκιμών συνήθως διαρκεί δεκάδες δευτερόλεπτα ή και λεπτά. Το σύστημα ελέγχου τύπου δίνει στον προγραμματιστή άμεση ανατροφοδότηση και του επιτρέπει να κάνει τη δουλειά του πιο γρήγορα. Δεν χρειάζεται πλέον να γράφει εύθραυστα και δύσκολα στη συντήρηση δοκιμές μονάδων που αντικαθιστούν πραγματικές οντότητες με μακέτες και επιδιορθώσεις απλώς και μόνο για να λαμβάνει πιο γρήγορα αποτελέσματα δοκιμών κώδικα.

Τα IDE και τα προγράμματα επεξεργασίας όπως το PyCharm ή το Visual Studio Code χρησιμοποιούν τη δύναμη των σχολιασμών τύπων για να παρέχουν στους προγραμματιστές τη συμπλήρωση κώδικα, την επισήμανση σφαλμάτων και την υποστήριξη για δομές γλώσσας που χρησιμοποιούνται συνήθως. Και αυτά είναι μόνο μερικά από τα οφέλη της πληκτρολόγησης. Για ορισμένους προγραμματιστές, όλα αυτά είναι το κύριο επιχείρημα υπέρ της πληκτρολόγησης. Αυτό είναι κάτι που ωφελείται αμέσως μετά την εφαρμογή. Αυτή η περίπτωση χρήσης για τύπους δεν απαιτεί ξεχωριστό σύστημα ελέγχου τύπου όπως το mypy, αν και θα πρέπει να σημειωθεί ότι το mypy βοηθά να διατηρούνται οι σχολιασμοί τύπων συνεπείς με τον κώδικα.

Υπόβαθρο mypy

Η ιστορία του mypy ξεκίνησε στο Ηνωμένο Βασίλειο, στο Cambridge, λίγα χρόνια πριν γίνω μέλος του Dropbox. Έχω ασχοληθεί, στο πλαίσιο της διδακτορικής μου έρευνας, με την ενοποίηση στατικά δακτυλογραφημένων και δυναμικών γλωσσών. Εμπνεύστηκα από ένα άρθρο για τη σταδιακή πληκτρολόγηση των Jeremy Siek και Walid Taha και από το έργο Typed Racket. Προσπάθησα να βρω τρόπους να χρησιμοποιήσω την ίδια γλώσσα προγραμματισμού για διάφορα έργα - από μικρά σενάρια έως βάσεις κώδικα που αποτελούνται από πολλά εκατομμύρια γραμμές. Ταυτόχρονα, ήθελα να διασφαλίσω ότι σε ένα έργο οποιασδήποτε κλίμακας, δεν θα χρειάζεται κανείς να κάνει πολύ μεγάλους συμβιβασμούς. Ένα σημαντικό μέρος όλων αυτών ήταν η ιδέα της σταδιακής μετάβασης από ένα μη τυποποιημένο πρωτότυπο έργο σε ένα ολοκληρωμένα ελεγμένο στατικά δακτυλογραφημένο τελικό προϊόν. Αυτές τις μέρες, αυτές οι ιδέες θεωρούνται σε μεγάλο βαθμό δεδομένες, αλλά το 2010 ήταν ένα πρόβλημα που εξακολουθούσε να διερευνάται ενεργά.

Η αρχική μου εργασία στον έλεγχο τύπων δεν στόχευε στην Python. Αντίθετα, χρησιμοποίησα μια μικρή «σπιτική» γλώσσα Αλόρε. Ακολουθεί ένα παράδειγμα που θα σας επιτρέψει να καταλάβετε για τι πράγμα μιλάμε (οι σχολιασμοί τύπου είναι προαιρετικοί εδώ):

def Fib(n as Int) as Int
  if n <= 1
    return n
  else
    return Fib(n - 1) + Fib(n - 2)
  end
end

Η χρήση μιας απλοποιημένης μητρικής γλώσσας είναι μια κοινή προσέγγιση που χρησιμοποιείται στην επιστημονική έρευνα. Αυτό συμβαίνει, κυρίως επειδή σας επιτρέπει να διεξάγετε γρήγορα πειράματα και επίσης λόγω του γεγονότος ότι ό,τι δεν έχει καμία σχέση με τη μελέτη μπορεί εύκολα να αγνοηθεί. Οι πραγματικές γλώσσες προγραμματισμού τείνουν να είναι φαινόμενα μεγάλης κλίμακας με πολύπλοκες υλοποιήσεις και αυτό επιβραδύνει τον πειραματισμό. Ωστόσο, τυχόν αποτελέσματα που βασίζονται σε μια απλοποιημένη γλώσσα φαίνονται λίγο ύποπτα, καθώς κατά την απόκτηση αυτών των αποτελεσμάτων ο ερευνητής μπορεί να θυσίασε σκέψεις σημαντικές για την πρακτική χρήση των γλωσσών.

Ο έλεγχος τύπων μου για το Alore φαινόταν πολλά υποσχόμενος, αλλά ήθελα να το δοκιμάσω πειραματιζόμενος με πραγματικό κώδικα, ο οποίος, θα μπορούσατε να πείτε, δεν ήταν γραμμένος στο Alore. Ευτυχώς για μένα, η γλώσσα Alore βασίστηκε σε μεγάλο βαθμό στις ίδιες ιδέες με την Python. Ήταν αρκετά εύκολο να ξαναφτιάξουμε το typechecker έτσι ώστε να μπορεί να λειτουργήσει με τη σύνταξη και τη σημασιολογία της Python. Αυτό μας επέτρεψε να δοκιμάσουμε τον έλεγχο τύπων σε κώδικα Python ανοιχτού κώδικα. Επιπλέον, έγραψα ένα transpiler για να μετατρέψω τον κώδικα γραμμένο σε Alore σε κώδικα Python και τον χρησιμοποίησα για να μεταφράσω τον κώδικα typechecker μου. Τώρα είχα ένα σύστημα ελέγχου τύπων γραμμένο σε Python που υποστήριζε ένα υποσύνολο της Python, κάποιο είδος αυτής της γλώσσας! (Ορισμένες αρχιτεκτονικές αποφάσεις που είχαν νόημα για το Alore ήταν ελάχιστα κατάλληλες για την Python, και αυτό είναι ακόμα αισθητό σε μέρη της βάσης κωδικών mypy.)

Στην πραγματικότητα, η γλώσσα που υποστηρίζεται από το σύστημα τύπων μου δεν μπορούσε να ονομαστεί Python σε αυτό το σημείο: ήταν μια παραλλαγή της Python λόγω ορισμένων περιορισμών της σύνταξης σχολιασμού τύπου Python 3.

Έμοιαζε με ένα μείγμα Java και Python:

int fib(int n):
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Μία από τις ιδέες μου εκείνη την εποχή ήταν να χρησιμοποιήσω σχολιασμούς τύπων για να βελτιώσω την απόδοση με τη μεταγλώττιση αυτού του είδους Python σε C, ή ίσως JVM bytecode. Έφτασα στο στάδιο να γράψω ένα πρωτότυπο μεταγλωττιστή, αλλά εγκατέλειψα αυτήν την ιδέα, καθώς ο έλεγχος τύπου από μόνος του φαινόταν αρκετά χρήσιμος.

Κατέληξα να παρουσιάζω το έργο μου στο PyCon 2013 στη Σάντα Κλάρα. Μίλησα επίσης για αυτό με τον Guido van Rossum, τον καλοπροαίρετο δικτάτορα Python για μια ζωή. Με έπεισε να αφήσω τη δική μου σύνταξη και να παραμείνω στην τυπική σύνταξη Python 3. Η Python 3 υποστηρίζει σχολιασμούς συναρτήσεων, έτσι το παράδειγμά μου θα μπορούσε να ξαναγραφτεί όπως φαίνεται παρακάτω, με αποτέλεσμα ένα κανονικό πρόγραμμα Python:

def fib(n: int) -> int:
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Έπρεπε να κάνω κάποιους συμβιβασμούς (πρώτα από όλα, θέλω να σημειώσω ότι επινόησα τη δική μου σύνταξη για αυτόν ακριβώς τον λόγο). Συγκεκριμένα, η Python 3.3, η πιο πρόσφατη έκδοση της γλώσσας εκείνη την εποχή, δεν υποστήριζε μεταβλητούς σχολιασμούς. Συζήτησα με τον Guido μέσω e-mail διάφορες δυνατότητες για συντακτικό σχεδιασμό τέτοιων σχολιασμών. Αποφασίσαμε να χρησιμοποιήσουμε σχόλια τύπου για μεταβλητές. Αυτό εξυπηρετούσε τον επιδιωκόμενο σκοπό, αλλά ήταν κάπως περίπλοκο (η Python 3.6 μας έδωσε μια πιο ωραία σύνταξη):

products = []  # type: List[str]  # Eww

Τα σχόλια πληκτρολόγησης ήταν επίσης χρήσιμα για την υποστήριξη της Python 2, η οποία δεν έχει ενσωματωμένη υποστήριξη για σχολιασμούς τύπων:

f fib(n):
    # type: (int) -> int
    if n <= 1:
        return n
    else:
        return fib(n - 1) + fib(n - 2)

Αποδείχθηκε ότι αυτές οι (και άλλες) ανταλλαγές δεν είχαν πραγματικά σημασία - τα οφέλη της στατικής πληκτρολόγησης σήμαιναν ότι οι χρήστες ξέχασαν σύντομα τη σύνταξη που δεν ήταν ιδανική. Δεδομένου ότι δεν χρησιμοποιήθηκαν ειδικές συντακτικές κατασκευές στον ελεγμένο κώδικα Python, τα υπάρχοντα εργαλεία Python και οι διαδικασίες επεξεργασίας κώδικα συνέχισαν να λειτουργούν κανονικά, καθιστώντας πολύ πιο εύκολο για τους προγραμματιστές να μάθουν το νέο εργαλείο.

Ο Guido με έπεισε επίσης να εγγραφώ στο Dropbox αφού ολοκλήρωσα τη μεταπτυχιακή μου διατριβή. Εδώ ξεκινά το πιο ενδιαφέρον μέρος της ιστορίας mypy.

Για να συνεχιστεί ...

Αγαπητοί αναγνώστες! Εάν χρησιμοποιείτε Python, πείτε μας για την κλίμακα των έργων που αναπτύσσετε σε αυτήν τη γλώσσα.

Η διαδρομή για τον έλεγχο πληκτρολόγησης 4 εκατομμυρίων γραμμών κώδικα Python. Μέρος 1
Η διαδρομή για τον έλεγχο πληκτρολόγησης 4 εκατομμυρίων γραμμών κώδικα Python. Μέρος 1

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο