annotationlib — Λειτουργικότητα για την ενδοσκόπηση των επισημάνσεων (annotations)

Added in version 3.14.

Πηγαίος κώδικας: Lib/annotationlib.py


Το module annotationlib παρέχει εργαλεία για την ενδοσκόπηση των annotations σε modules, κλάσεις και συναρτήσεις.

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

Αυτό το module υποστηρίζει την ανάκτηση των επισημάνσεων (annotations) σε τρεις κύριες μορφές (δείτε το Format), κάθε μία από τις οποίες λειτουργεί καλύτερα για διαφορετικές περιπτώσεις χρήσης:

  • Το VALUE αξιολογεί τις επισημάνσεις (annotations) και επιστρέφει την τιμή τους. Αυτή είναι η πιο απλή μορφή για να εργαστεί κανείς, αλλά μπορεί να εγείρει σφάλματα, για παράδειγμα αν οι επισημάνσεις (annotations) περιέχουν αναφορές σε ονόματα που δεν έχουν οριστεί.

  • Το FORWARDREF επιστρέφει αντικείμενα ForwardRef για επισημάνσεις (annotations) που δεν μπορούν να επιλυθούν, επιτρέποντάς σας να επιθεωρήσετε τις επισημάνσεις (annotations) χωρίς να τα αξιολογήσετε. Αυτό είναι χρήσιμο όταν χρειάζεται να εργαστείτε με επισημάνσεις (annotations) που μπορεί να περιέχουν ανεπίλυτες αναφορές προς τα εμπρός.

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

Η συνάρτηση get_annotations() είναι το κύριο σημείο εισόδου για την ανάκτηση των επισημάνσεων (annotations). Δοθείσας μιας συνάρτησης, μιας κλάσης ή ενός module, επιστρέφει ένα λεξικό των επισημάνσεων (annotations) στη ζητούμενη μορφή. Αυτό το module παρέχει επίσης τη λειτουργικότητα για την άμεση εργασία με την annotate function, που χρησιμοποιείται για την αξιολόγηση των επισημάνσεων (annotations), όπως οι get_annotate_from_class_namespace() και call_annotate_function(), καθώς και τη συνάρτηση call_evaluate_function() για εργασία με evaluate functions.

Προσοχή

Η περισσότερη λειτουργικότητα σε αυτό το module μπορεί να εκτελέσει αυθαίρετο κώδικα· δείτε την ενότητα ασφαλείας για περισσότερες πληροφορίες.

Δείτε επίσης

Το PEP 649 πρότεινε το τρέχον μοντέλο για το πώς λειτουργούν οι επισημάνσεις (annotations) στην Python.

Το PEP 749 επέκτεινε διάφορες πτυχές του PEP 649 και εισήγαγε το module annotationlib.

Το Annotations Best Practices παρέχει βέλτιστες πρακτικές για την εργασία με τις επισημάνσεις (annotations).

Το typing-extensions παρέχει ένα backport της get_annotations() που λειτουργεί σε προγενέστερες εκδόσεις της Python.

Σημασιολογία των επισημάνσεων (annotations)

Ο τρόπος αξιολόγησης των επισημάνσεων (annotations) έχει αλλάξει κατά τη διάρκεια της ιστορίας της Python 3 και επί του παρόντος εξαρτάται ακόμα από μια εισαγωγή future. Υπήρξαν μοντέλα εκτέλεσης για τις επισημάνσεις (annotations):

  • Βασική σημασιολογία (προεπιλογή στην Python από την έκδοση 3.0 έως την 3.13· δείτε τα PEP 3107 και PEP 526): οι επισημάνσεις (annotations) αξιολογούνται άμεσα, όπως συναντώνται στον πηγαίο κώδικα.

  • Επισημάνσεις (annotations) ως συμβολοσειρές (χρησιμοποιούνται με το from __future__ import annotations από την Python 3.7 και μετά· δείτε το PEP 563): οι επισημάνσεις (annotations) αποθηκεύονται μόνο ως συμβολοσειρές.

  • Αναβαλλόμενη αξιολόγηση (προεπιλογή από την Python 3.14 και μετά· δείτε τα PEP 649 και PEP 749): οι επισημάνσεις (annotations) αξιολογούνται νωχελικά, μόνο όταν προσπελαύνονται.

Ως παράδειγμα, εξετάστε το ακόλουθο πρόγραμμα:

def func(a: Cls) -> None:
    print(a)

class Cls: pass

print(func.__annotations__)

Αυτό θα συμπεριφερθεί ως εξής:

  • Υπό τη βασική σημασιολογία (Python 3.13 και προγενέστερες), θα εγείρει ένα NameError στη γραμμή όπου ορίζεται η func, επειδή το Cls είναι ένα μη ορισμένο όνομα σε εκείνο το σημείο.

  • Υπό τις επισημάνσεις (annotations) ως συμβολοσειρές (αν χρησιμοποιείται το from __future__ import annotations), θα εκτυπώσει {'a': 'Cls', 'return': 'None'}.

  • Υπό την αναβαλλόμενη αξιολόγηση (Python 3.14 και μεταγενέστερες), θα εκτυπώσει {'a': <class 'Cls'>, 'return': None}.

Η βασική σημασιολογία χρησιμοποιήθηκε όταν οι επισημάνσεις (annotations) συναρτήσεων εισήχθησαν για πρώτη φορά στην Python 3.0 (από το PEP 3107), επειδή ήταν ο απλούστερος, πιο προφανής τρόπος υλοποίησης των επισημάνσεων (annotations). Το ίδιο μοντέλο εκτέλεσης χρησιμοποιήθηκε όταν εισήχθησαν οι επισημάνσεις (annotations) μεταβλητών στην Python 3.6 (από το PEP 526). Ωστόσο, η βασική σημασιολογία προκάλεσε προβλήματα κατά τη χρήση των επισημάνσεων (annotations) ως υποδείξεων τύπων, όπως η ανάγκη αναφοράς σε ονόματα που δεν έχουν ακόμη οριστεί όταν συναντάται η επισήμανση. Επιπλέον, υπήρξαν προβλήματα απόδοσης με την εκτέλεση των επισημάνσεων (annotations) κατά τον χρόνο εισαγωγής του module. Επομένως, στην Python 3.7, το PEP 563 εισήγαγε τη δυνατότητα αποθήκευσης των επισημάνσεων (annotations) ως συμβολοσειρές χρησιμοποιώντας τη σύνταξη from __future__ import annotations. Το σχέδιο τότε ήταν να γίνει τελικά αυτή η συμπεριφορά η προεπιλογή, αλλά εμφανίστηκε ένα πρόβλημα: οι επισημάνσεις (annotations) ως συμβολοσειρές είναι πιο δύσκολο να επεξεργαστούν για όσα άτομα κάνουν ενδοσκόπηση στις επισημάνσεις (annotations) κατά τον χρόνο εκτέλεσης. Σαν εναλλακτική πρόταση, το PEP 649, εισήγαγε το τρίτο μοντέλο εκτέλεσης, την αναβαλλόμενη αξιολόγηση, και υλοποιήθηκε στην Python 3.14. Οι επισημάνσεις (annotations) ως συμβολοσειρές εξακολουθούν να χρησιμοποιούνται αν υπάρχει το from __future__ import annotations, αλλά αυτή η συμπεριφορά θα αφαιρεθεί τελικά.

Κλάσεις

class annotationlib.Format

Μία IntEnum που περιγράφει τις μορφές στις οποίες μπορούν να επιστραφούν οι επισημάνσεις (annotations). Τα μέλη του enum, ή οι ισοδύναμες ακέραιες τιμές τους, μπορούν να περαστούν στη get_annotations() και σε άλλες συναρτήσεις αυτού του module, καθώς και σε συναρτήσεις __annotate__.

VALUE = 1

Οι τιμές είναι το αποτέλεσμα της αξιολόγησης των εκφράσεων σχολιασμού.

VALUE_WITH_FAKE_GLOBALS = 2

Ειδική τιμή που χρησιμοποιείται για να σηματοδοτήσει ότι μια συνάρτηση σχολιασμού αξιολογείται σε ένα ειδικό περιβάλλον με ψεύτικα καθολικά (fake globals). Όταν περνιέται αυτή η τιμή, οι συναρτήσεις σχολιασμού θα πρέπει είτε να επιστρέψουν την ίδια τιμή όπως για τη μορφή Format.VALUE, είτε να εγείρουν NotImplementedError για να σηματοδοτήσουν ότι δεν υποστηρίζουν εκτέλεση σε αυτό το περιβάλλον. Αυτή η μορφή χρησιμοποιείται μόνο εσωτερικά και δεν πρέπει να περνιέται στις συναρτήσεις αυτού του module.

FORWARDREF = 3

Οι τιμές είναι πραγματικές τιμές σχολιασμού (όπως στη μορφή Format.VALUE) για ορισμένες τιμές, και αντιπρόσωποι (proxies) ForwardRef για μη ορισμένες τιμές. Τα πραγματικά αντικείμενα μπορεί να περιέχουν αναφορές σε αντικείμενα αντιπροσώπων ForwardRef.

STRING = 4

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

Οι ακριβείς τιμές αυτών των συμβολοσειρών ενδέχεται να αλλάξουν σε μελλοντικές εκδόσεις της Python.

Added in version 3.14.

class annotationlib.ForwardRef

Ένα αντικείμενο αντιπροσώπου για αναφορές προς τα εμπρός σε επισημάνσεις (annotations).

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

__forward_arg__

Μια συμβολοσειρά που περιέχει τον κώδικα που αξιολογήθηκε για να παραχθεί η ForwardRef. Η συμβολοσειρά μπορεί να μην είναι ακριβώς ισοδύναμη με τον αρχικό πηγαίο κώδικα.

evaluate(*, owner=None, globals=None, locals=None, type_params=None, format=Format.VALUE)

Αξιολογεί την αναφορά προς τα εμπρός, επιστρέφοντας την τιμή της.

Αν το όρισμα format είναι VALUE (η προεπιλογή), αυτή η μέθοδος μπορεί να προκαλέσει μια εξαίρεση, όπως την NameError, αν η αναφορά προς τα εμπρός αναφέρεται σε ένα όνομα που δεν μπορεί να επιλυθεί. Τα ορίσματα αυτής της μεθόδου μπορούν να χρησιμοποιηθούν για να παρέχουν συνδέσεις για ονόματα που διαφορετικά θα ήταν μη ορισμένα. Αν το όρισμα format είναι FORWARDREF, η μέθοδος δεν θα προκαλέσει ποτέ εξαίρεση, αλλά μπορεί να επιστρέψει ένα στιγμιότυπο της ForwardRef. Για παράδειγμα, αν το αντικείμενο αναφοράς προς τα εμπρός περιέχει τον κώδικα list[undefined], όπου undefined είναι ένα όνομα που δεν έχει οριστεί, η αξιολόγησή του με τη μορφή FORWARDREF θα επιστρέψει list[ForwardRef('undefined')]. Αν το όρισμα format είναι STRING, η μέθοδος θα επιστρέψει __forward_arg__.

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

Οι παράμετροι globals, locals και type_params παρέχουν έναν πιο ακριβή μηχανισμό για τον επηρεασμό των ονομάτων που είναι διαθέσιμα όταν αξιολογείται η ForwardRef. Τα globals και locals περνιούνται στη eval(), αντιπροσωπεύοντας τους καθολικούς και τοπικούς χώρους ονομάτων στους οποίους αξιολογείται το όνομα. Η παράμετρος type_params είναι σχετική για αντικείμενα που δημιουργούνται χρησιμοποιώντας την εγγενή σύνταξη για γενικευμένες κλάσεις και συναρτήσεις. Είναι ένα tuple από παραμέτρους τύπου που είναι εντός εμβέλειας όσο η αναφορά προς τα εμπρός αξιολογείται. Για παράδειγμα, αν αξιολογείται μία ForwardRef που ανακτήθηκε από έναν μια επισήμανση (annotation), που βρίσκεται στον χώρο ονομάτων κλάσης μιας γενικευμένης κλάσης C, το type_params θα πρέπει να οριστεί ως C.__type_params__.

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

Αν δεν παρέχεται κανένα από τα owner, globals, locals ή type_params και η ForwardRef δεν περιέχει πληροφορίες σχετικά με την προέλευσή της, χρησιμοποιούνται κενά καθολικά και τοπικά λεξικά.

Added in version 3.14.

Συναρτήσεις

annotationlib.annotations_to_string(annotations)

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

Για παράδειγμα, χρησιμοποιείται για την υλοποίηση της STRING για κλάσεις typing.TypedDict που δημιουργούνται μέσω της συναρτησιακής σύνταξης:

>>> from typing import TypedDict
>>> Movie = TypedDict("movie", {"name": str, "year": int})
>>> get_annotations(Movie, format=Format.STRING)
{'name': 'str', 'year': 'int'}

Added in version 3.14.

annotationlib.call_annotate_function(annotate, format, *, owner=None)

Καλεί τη συνάρτηση σχολιασμού annotate με το δοθέν format, ένα μέλος του enum Format, και επιστρέφει το λεξικό των επισημάνσεων (annotations) που παράγεται από τη συνάρτηση.

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

Το owner είναι το αντικείμενο που κατέχει τη συνάρτηση annotate, συνήθως μια συνάρτηση, κλάση ή module. Αν παρέχεται, χρησιμοποιείται στη μορφή FORWARDREF για να παράγει ένα αντικείμενο της ForwardRef που μεταφέρει περισσότερες πληροφορίες.

Δείτε επίσης

Το PEP 649 περιέχει μια εξήγηση της τεχνικής υλοποίησης που χρησιμοποιείται από αυτή τη συνάρτηση.

Added in version 3.14.

annotationlib.call_evaluate_function(evaluate, format, *, owner=None)

Καλεί τη συνάρτηση αξιολόγησης evaluate με ο δοθέν format, ένα μέλος του enum Format, και επιστρέφει την τιμή που παράγεται από τη συνάρτηση. Είναι παρόμοιο με την call_annotate_function(), αλλά η τελευταία επιστρέφει πάντα ένα λεξικό που αντιστοιχίζει συμβολοσειρές σε επισημάνσεις (annotations), ενώ αυτή η συνάρτηση επιστρέφει μια ενιαία τιμή.

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

Το owner είναι το αντικείμενο που κατέχει τη συνάρτηση αξιολόγησης, όπως το αντικείμενο ψευδωνύμου τύπου ή μεταβλητής τύπου.

Το format μπορεί να χρησιμοποιηθεί για τον έλεγχο της μορφής στην οποία επιστρέφεται η τιμή:

>>> type Alias = undefined
>>> call_evaluate_function(Alias.evaluate_value, Format.VALUE)
Traceback (most recent call last):
...
NameError: name 'undefined' is not defined
>>> call_evaluate_function(Alias.evaluate_value, Format.FORWARDREF)
ForwardRef('undefined')
>>> call_evaluate_function(Alias.evaluate_value, Format.STRING)
'undefined'

Added in version 3.14.

annotationlib.get_annotate_from_class_namespace(namespace)

Ανακτά τη συνάρτηση σχολιασμού από ένα λεξικό χώρου ονομάτων κλάσης namespace. Επιστρέφει None αν ο χώρος ονομάτων δεν περιέχει συνάρτηση annotate. Eίναι κυρίως χρήσιμο πριν η κλάση δημιουργηθεί πλήρως (π.χ., σε μια μετάκληση)· μετά την ύπαρξη της κλάσης, η συνάρτηση σχολιασμού μπορεί να ανακτηθεί με την cls.__annotate__. Δείτε παρακάτω για ένα παράδειγμα χρήσης αυτής της συνάρτησης σε μια μετάκληση.

Added in version 3.14.

annotationlib.get_annotations(obj, *, globals=None, locals=None, eval_str=False, format=Format.VALUE)

Υπολογίζει το λεξικό των επισημάνσεων (annotations) για ένα αντικείμενο.

Το obj μπορεί να είναι μια καλέσιμη οντότητα, μία κλάση, ένα module ή άλλο αντικείμενο με χαρακτηριστικά __annotate__ ή __annotations__. Το πέρασμα οποιουδήποτε άλλου αντικειμένου κάνει raise την TypeError.

Η παράμετρος format ελέγχει τη μορφή στην οποία επιστρέφονται οι επισημάνσεις (annotations) και πρέπει να είναι μέλος του enum Format ή το ακέραιο ισοδύναμό του. Οι διάφορες μορφές λειτουργούν ως εξής:

  • VALUE: Δοκιμάζεται πρώτα το object.__annotations__· αν αυτό δεν υπάρχει, καλείται η συνάρτηση object.__annotate__ αν υπάρχει.

  • FORWARDREF: Αν το object.__annotations__ υπάρχει και μπορεί να αξιολογηθεί επιτυχώς, χρησιμοποιείται· διαφορετικά, καλείται η συνάρτηση object.__annotate__. Αν ούτε αυτή υπάρχει, δοκιμάζεται ξανά το object.__annotations__ και κάθε σφάλμα από την πρόσβαση σε αυτό γίνεται raise ξανά.

    • Όταν καλείται το object.__annotate__, καλείται πρώτα με το FORWARDREF. Αν αυτό δεν είναι υλοποιημένο, στη συνέχεια ελέγχει αν υποστηρίζεται το VALUE_WITH_FAKE_GLOBALS και το χρησιμοποιεί στο περιβάλλον με τα ψεύτικα καθολικά. Αν καμία από αυτές τις μορφές δεν υποστηρίζεται, θα καταφύγει στη χρήση του VALUE. Αν το VALUE αποτύχει, το σφάλμα από αυτή την κλήση θα γίνει raise.

  • STRING: Αν υπάρχει το object.__annotate__, καλείται πρώτο· διαφορετικά, χρησιμοποιείται το object.__annotations__ και μετατρέπεται σε συμβολοσειρά χρησιμοποιώντας την annotations_to_string().

    • Όταν καλείται το object.__annotate__, καλείται πρώτα με STRING. Αν αυτό δεν είναι υλοποιημένο, στη συνέχεια ελέγχει αν υποστηρίζεται το VALUE_WITH_FAKE_GLOBALS και το χρησιμοποιεί στο περιβάλλον με τα ψεύτικα καθολικά. Αν καμία από αυτές τις μορφές δεν υποστηρίζεται, θα καταφύγει στη χρήση του VALUE με το αποτέλεσμα να μετατρέπεται χρησιμοποιώντας την annotations_to_string(). Αν το VALUE αποτύχει, το σφάλμα από αυτή την κλήση θα γίνει raise.

Επιστρέφει ένα λεξικό. Η get_annotations() επιστρέφει ένα νέο λεξικό κάθε φορά που καλείται· αν κληθεί δύο φορές στο ίδιο αντικείμενο θα επιστρέψει δύο διαφορετικά αλλά ισοδύναμα λεξικά.

Αυτή η συνάρτηση χειρίζεται αρκετές λεπτομέρειες για εσάς:

  • Αν το eval_str είναι αληθές, οι τιμές τύπου str θα απομετατραπούν από συμβολοσειρές χρησιμοποιώντας τη eval(). Προορίζεται για χρήση με επισημάνσεις (annotations) ως συμβολοσειρές (from __future__ import annotations). Είναι λάθος να οριστεί το eval_str ως αληθές με μορφές διαφορετικές από το Format.VALUE.

  • Αν το obj δεν έχει λεξικό επισημάνσεων (annotations), επιστρέφει ένα κενό λεξικό. (Οι συναρτήσεις και οι μέθοδοι έχουν πάντα λεξικό επισημάνσεων· οι κλάσεις, τα modules και άλλοι τύποι καλέσιμων μπορεί να μην έχουν.)

  • Αγνοεί τις κληρονομημένες επισημάνσεις (annotations) σε κλάσεις, καθώς και τις επισημάνσεις (annotations) σε μετακλάσεις. Αν μια κλάση δεν έχει το δικό της λεξικό επισημάνσεων (annotations), επιστρέφει ένα κενό λεξικό.

  • Όλες οι προσβάσεις σε μέλη αντικειμένων και τιμές λεξικών γίνονται χρησιμοποιώντας τις getattr() και dict.get() για λόγους ασφαλείας.

Το eval_str ελέγχει αν οι τιμές τύπου str αντικαθίστανται ή όχι με το αποτέλεσμα της κλήσης της eval() σε αυτές τις τιμές:

  • Αν το eval_str είναι αληθές, η eval() καλείται σε τιμές τύπου str. (Σημειώστε ότι η get_annotations() δεν πιάνει εξαιρέσεις· αν η eval() κάνει raise μια εξαίρεση, θα ξετυλίξει τη στοίβα πέρα από την κλήση της get_annotations().)

  • Αν το eval_str είναι ψευδές (η προεπιλογή), οι τιμές τύπου str παραμένουν αμετάβλητες.

Τα globals και locals περνιούνται στην eval()· δείτε την τεκμηρίωση της eval() για περισσότερες πληροφορίες. Αν το globals ή το locals είναι None, αυτή η συνάρτηση μπορεί να αντικαταστήσει αυτή την τιμή με μια προεπιλογή ειδική για το πλαίσιο, εξαρτώμενη από το type(obj):

  • Αν το obj είναι module, η προεπιλογή για το globals είναι obj.__dict__.

  • Αν το obj είναι κλάση, η προεπιλογή για το globals είναι sys.modules[obj.__module__].__dict__ και η προεπιλογή για το locals είναι ο χώρος ονομάτων κλάσης του obj.

  • Αν το obj είναι καλέσιμο, η προεπιλογή για το globals είναι obj.__globals__, παρόλο που αν το obj είναι μια περιτυλιγμένη συνάρτηση (χρησιμοποιώντας functools.update_wrapper()) ή ένα αντικείμενο της functools.partial, ξετυλίγεται μέχρι να βρεθεί μια μη περιτυλιγμένη συνάρτηση.

Η κλήση της get_annotations() είναι η βέλτιστη πρακτική για την πρόσβαση στο λεξικό των επισημάνσεων (annotations) οποιουδήποτε αντικειμένου. Δείτε το Annotations Best Practices για περισσότερες πληροφορίες σχετικά με τις βέλτιστες πρακτικές των επισημάνσεων (annotations).

>>> def f(a: int, b: str) -> float:
...     pass
>>> get_annotations(f)
{'a': <class 'int'>, 'b': <class 'str'>, 'return': <class 'float'>}

Added in version 3.14.

annotationlib.type_repr(value)

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

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

Added in version 3.14.

Συνταγές

Χρήση των επισημάνσεων (annotations) σε μετακλάση

Μια μετακλάση μπορεί να θέλει να επιθεωρήσει ή ακόμη και να τροποποιήσει τις επισημάνσεις (annotations) στο σώμα μιας κλάσης κατά τη δημιουργία της κλάσης. Αυτό απαιτεί την ανάκτηση των επισημάνσεων (annotations) από το λεξικό χώρου ονομάτων της κλάσης. Για κλάσεις που δημιουργούνται με το from __future__ import annotations, οι επισημάνσεις (annotations) θα βρίσκονται στο κλειδί __annotations__ του λεξικού. Για άλλες κλάσεις με επισημάνσεις (annotations), η get_annotate_from_class_namespace() μπορεί να χρησιμοποιηθεί για να πάρει τη συνάρτηση annotate, και η call_annotate_function() μπορεί να χρησιμοποιηθεί για να την καλέσει και να ανακτήσει τις επισημάνσεις (annotations). Η χρήση της μορφής FORWARDREF συνήθως θα είναι η καλύτερη, επειδή επιτρέπει στις επισημάνσεις (annotations) να αναφέρονται σε ονόματα που δεν μπορούν ακόμη να επιλυθούν όταν δημιουργείται η κλάση.

Για να τροποποιήσετε τις επισημάνσεις (annotations), είναι καλύτερο να δημιουργήσετε μια περιτυλιγμένη συνάρτηση annotate που καλεί την αρχική συνάρτηση annotate, κάνει τις απαραίτητες προσαρμογές και επιστρέφει το αποτέλεσμα.

Παρακάτω είναι ένα παράδειγμα μιας μετακλάσης, που φιλτράρει όλες τις επισημάνσεις (annotations) typing.ClassVar από την κλάση και τα τοποθετεί σε ένα ξεχωριστό χαρακτηριστικό:

import annotationlib
import typing

class ClassVarSeparator(type):
   def __new__(mcls, name, bases, ns):
      if "__annotations__" in ns:  # from __future__ import annotations
         annotations = ns["__annotations__"]
         classvar_keys = {
            key for key, value in annotations.items()
            # Use string comparison for simplicity; a more robust solution
            # could use annotationlib.ForwardRef.evaluate
            if value.startswith("ClassVar")
         }
         classvars = {key: annotations[key] for key in classvar_keys}
         ns["__annotations__"] = {
            key: value for key, value in annotations.items()
            if key not in classvar_keys
         }
         wrapped_annotate = None
      elif annotate := annotationlib.get_annotate_from_class_namespace(ns):
         annotations = annotationlib.call_annotate_function(
            annotate, format=annotationlib.Format.FORWARDREF
         )
         classvar_keys = {
            key for key, value in annotations.items()
            if typing.get_origin(value) is typing.ClassVar
         }
         classvars = {key: annotations[key] for key in classvar_keys}

         def wrapped_annotate(format):
            annos = annotationlib.call_annotate_function(annotate, format, owner=typ)
            return {key: value for key, value in annos.items() if key not in classvar_keys}

      else:  # no annotations
         classvars = {}
         wrapped_annotate = None
      typ = super().__new__(mcls, name, bases, ns)

      if wrapped_annotate is not None:
         # Wrap the original __annotate__ with a wrapper that removes ClassVars
         typ.__annotate__ = wrapped_annotate
      typ.classvars = classvars  # Store the ClassVars in a separate attribute
      return typ

Creating a custom callable annotate function

Custom annotate functions may be literal functions like those automatically generated for functions, classes, and modules. Or, they may wish to utilise the encapsulation provided by classes, in which case any callable can be used as an annotate function.

To provide the VALUE, STRING, or FORWARDREF formats directly, an annotate function must provide the following attribute:

  • A callable __call__ with signature __call__(format, /) -> dict, that does not raise a NotImplementedError when called with a supported format.

To provide the VALUE_WITH_FAKE_GLOBALS format, which is used to automatically generate STRING or FORWARDREF if they are not supported directly, annotate functions must provide the following attributes:

  • A callable __call__ with signature __call__(format, /) -> dict, that does not raise a NotImplementedError when called with VALUE_WITH_FAKE_GLOBALS.

  • A code object __code__ containing the compiled code for the annotate function.

  • Optional: A tuple of the function’s positional defaults __kwdefaults__, if the function represented by __code__ uses any positional defaults.

  • Optional: A dict of the function’s keyword defaults __defaults__, if the function represented by __code__ uses any keyword defaults.

  • Optional: All other function attributes.

class Annotate:
    called_formats = []

    def __call__(self, format=None, /, *, _self=None):
        # When called with fake globals, `_self` will be the
        # actual self value, and `self` will be the format.
        if _self is not None:
            self, format = _self, self

        self.called_formats.append(format)
        if format <= 2:  # VALUE or VALUE_WITH_FAKE_GLOBALS
            return {"x": MyType}
        raise NotImplementedError

    __code__ = __call__.__code__
    __defaults__ = (None,)
    __kwdefaults__ = property(lambda self: dict(_self=self))

    __globals__ = {}
    __builtins__ = {}
    __closure__ = None

This can then be called with:

>>> from annotationlib import call_annotate_function, Format
>>> call_annotate_function(Annotate(), format=Format.STRING)
{'x': 'MyType'}

Or used as the annotate function for an object:

>>> from annotationlib import get_annotations, Format
>>> class C:
...   pass
>>> C.__annotate__ = Annotate()
>>> get_annotations(Annotate(), format=Format.STRING)
{'x': 'MyType'}

Περιορισμοί της μορφής STRING

Η μορφή STRING προορίζεται για να προσεγγίζει τον πηγαίο κώδικα του σχολιασμού, αλλά η στρατηγική υλοποίησης που χρησιμοποιείται σημαίνει ότι δεν είναι πάντα δυνατή η ανάκτηση του ακριβούς πηγαίου κώδικα.

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

Δεύτερον, ο μετατροπέας συμβολοσειράς μπορεί να παρεμβάλει σχεδόν όλες τις πράξεις που περιλαμβάνουν ονόματα που αναζητούνται σε κάποια εμβέλεια, αλλά δεν μπορεί να παρεμβάλει πράξεις που λειτουργούν πλήρως πάνω σε σταθερές. Ως επακόλουθο, αυτό σημαίνει επίσης ότι δεν είναι ασφαλές να ζητείται η μορφή STRING σε μη αξιόπιστο κώδικα: η Python είναι αρκετά ισχυρή ώστε να μπορεί να επιτευχθεί αυθαίρετη εκτέλεση κώδικα ακόμη και χωρίς πρόσβαση σε global ή builtins. Για παράδειγμα:

>>> def f(x: (1).__class__.__base__.__subclasses__()[-1].__init__.__builtins__["print"]("Hello world")): pass
...
>>> annotationlib.get_annotations(f, format=annotationlib.Format.STRING)
Hello world
{'x': 'None'}

Σημείωση

Αυτό το συγκεκριμένο παράδειγμα λειτουργεί κατά τη στιγμή της συγγραφής, αλλά βασίζεται σε λεπτομέρειες υλοποίησης και δεν είναι εγγυημένο ότι θα λειτουργεί στο μέλλον.

Μεταξύ των διαφορετικών ειδών εκφράσεων που υπάρχουν στην Python, όπως αναπαρίστανται από το module ast, ορισμένες εκφράσεις υποστηρίζονται, που σημαίνει ότι η μορφή STRING μπορεί γενικά να ανακτήσει τον αρχικό πηγαίο κώδικα· άλλες δεν υποστηρίζονται, που σημαίνει ότι μπορεί να οδηγήσουν σε λανθασμένη έξοδο ή σφάλμα.

Τα ακόλουθα υποστηρίζονται (μερικές φορές με επιφυλάξεις):

  • ast.BinOp

  • ast.UnaryOp

  • ast.Dict (εκτός αν χρησιμοποιείται αποσυσκευασία **)

  • ast.Set

  • ast.Compare

  • ast.Call (εκτός αν χρησιμοποιείται αποσυσκευασία **)

  • ast.Constant (αν και όχι η ακριβής αναπαράσταση της σταθεράς· για παράδειγμα, οι ακολουθίες διαφυγής σε συμβολοσειρές χάνονται· οι δεκαεξαδικοί αριθμοί μετατρέπονται σε δεκαδικούς)

  • ast.Attribute (υποθέτοντας ότι η τιμή δεν είναι σταθερά)

  • ast.Subscript (υποθέτοντας ότι η τιμή δεν είναι σταθερά)

  • ast.Starred (αποσυσκευασία *)

  • ast.Name

  • ast.List

  • ast.Tuple

  • ast.Slice

Τα ακόλουθα δεν υποστηρίζονται, αλλά εγείρει ένα ενημερωτικό σφάλμα όταν συναντώνται από τον μετατροπέα συμβολοσειράς:

  • ast.FormattedValue (f-strings· το σφάλμα δεν ανιχνεύεται αν χρησιμοποιούνται προδιαγραφές μετατροπής όπως !r)

  • ast.JoinedStr (f-strings)

Τα ακόλουθα δεν υποστηρίζονται και οδηγούν σε λανθασμένη έξοδο:

Τα ακόλουθα δεν επιτρέπονται σε εμβέλειες των επισημάνσεων (annotations) και επομένως δεν είναι σχετικά:

Περιορισμοί της μορφής FORWARDREF

Η μορφή FORWARDREF στοχεύει στην παραγωγή πραγματικών τιμών όσο το δυνατόν περισσότερο, αντικαθιστώντας οτιδήποτε δεν μπορεί να επιλυθεί με αντικείμενα της ForwardRef. Επηρεάζεται γενικά από τους ίδιους περιορισμούς με τη μορφή STRING: οι επισημάνσεις (annotations) που εκτελούν πράξεις σε λεκτικές σταθερές ή που χρησιμοποιούν μη υποστηριζόμενους τύπους εκφράσεων μπορεί να κάνουν raise εξαιρέσεις όταν αξιολογούνται χρησιμοποιώντας τη μορφή FORWARDREF.

Παρακάτω είναι μερικά παραδείγματα της συμπεριφοράς με μη υποστηριζόμενες εκφράσεις:

>>> from annotationlib import get_annotations, Format
>>> def zerodiv(x: 1 / 0): ...
>>> get_annotations(zerodiv, format=Format.STRING)
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
>>> get_annotations(zerodiv, format=Format.FORWARDREF)
Traceback (most recent call last):
  ...
ZeroDivisionError: division by zero
>>> def ifexp(x: 1 if y else 0): ...
>>> get_annotations(ifexp, format=Format.STRING)
{'x': '1'}

Επιπτώσεις ασφαλείας από την ενδοσκόπηση των επισημάνσεων (annotations)

Η περισσότερη λειτουργικότητα σε αυτό το module περιλαμβάνει την εκτέλεση κώδικα που σχετίζεται με επισημάνσεις (annotations), ο οποίος μπορεί στη συνέχεια να κάνει αυθαίρετα πράγματα. Για παράδειγμα, η get_annotations() μπορεί να καλέσει μια αυθαίρετη συνάρτηση annotate, και η ForwardRef.evaluate() μπορεί να καλέσει την eval() σε μια αυθαίρετη συμβολοσειρά. Ο κώδικας που περιέχεται σε μια επισήμανση (annotation) μπορεί να κάνει αυθαίρετες κλήσεις συστήματος, να μπει σε ατέρμονο βρόχο ή να εκτελέσει οποιαδήποτε άλλη λειτουργία. Αυτό ισχύει επίσης για κάθε πρόσβαση στο χαρακτηριστικό __annotations__, και για διάφορες συναρτήσεις στο module typing που εργάζονται με επισημάνσεις (annotations), όπως η typing.get_type_hints().

Κάθε ζήτημα ασφαλείας, που προκύπτει από αυτό, ισχύει επίσης αμέσως μετά την εισαγωγή κώδικα που μπορεί να περιέχει μη αξιόπιστες επισημάνσεις (annotations): η εισαγωγή κώδικα μπορεί πάντα να προκαλέσει την εκτέλεση αυθαίρετων λειτουργιών. Ωστόσο, είναι μη ασφαλές να γίνονται αποδεκτές συμβολοσειρές ή άλλη είσοδος από μη αξιόπιστη πηγή και να περνιούνται σε οποιοδήποτε από τα API για ενδοσκόπηση των επισημάνσεων (annotations), για παράδειγμα με την επεξεργασία ενός λεξικού __annotations__ ή την άμεση δημιουργία ενός αντικειμένου της ForwardRef.