5. Το σύστημα εισαγωγής
***********************

Ο κώδικας Python σε ένα *module* αποκτά πρόσβαση στον κώδικα ενός
άλλου module μέσω της διαδικασίας του *importing*. Η δήλωση "import"
είναι ο πιο συνηθισμένος τρόπος ενεργοποίησης του μηχανισμού import,
αλλά δεν είναι ο μόνος. Συναρτήσεις όπως η "importlib.import_module()"
και η ενσωματωμένη "__import__()" μπορούν επίσης να χρησιμοποιηθούν
για την ενεργοποίηση του μηχανισμού εισαγωγής.

Η δήλωση "import" συνδυάζει δύο λειτουργίες· αναζητά το module με το
συγκεκριμένο όνομα και στη συνέχεια συνδέει τα αποτελέσματα αυτής της
αναζήτησης σε ένα όνομα στο τοπικό scope. Η λειτουργία αναζήτησης της
δήλωσης "import" ορίζεται ως κλήση της συνάρτησης "__import__()", με
τα κατάλληλα ορίσματα. Η τιμή επιστροφής της "__import__()"
χρησιμοποιείται για να εκτελεστεί η λειτουργία δέσμευσης ονόματος της
δήλωσης "import". Δείτε τη δήλωση "import" για τις ακριβείς
λεπτομέρειες αυτής της λειτουργίας.

Μια άμεση κλήση της "__import__()" εκτελεί μόνο την αναζήτηση του
module και, αν βρεθεί, τη λειτουργία δημιουργίας του. Παρότι μπορεί να
προκύψουν ορισμένες παρενέργειες, όπως η εισαγωγή των γονικών πακέτων
και η ενημέρωση διάφορων προσωρινών μνημών (συμπεριλαμβανομένου του
"sys.modules"), μόνο η δήλωση "import" εκτελεί λειτουργία δέσμευσης
ονόματος.

Όταν εκτελείται μια δήλωση "import", καλείται η τυπική ενσωματωμένη
συνάρτηση "__import__()". Άλλοι μηχανισμοί ενεργοποίησης του
συστήματος εισαγωγής (όπως η "importlib.import_module()") μπορεί να
επιλέξουν να παρακάμψουν τη "__import__()" και να χρησιμοποιήσουν
δικές τους λύσεις για την υλοποίηση της σημασιολογίας της εισαγωγής.

Όταν εισάγεται ένα module για πρώτη φορά, η Python το αναζητά και, αν
το βρει, δημιουργεί ένα αντικείμενο module [1], αρχικοποιώντας το. Αν
το module με το συγκεκριμένο όνομα δεν μπορεί να βρεθεί, γίνεται raise
η εξαίρεση "ModuleNotFoundError". Η Python υλοποιεί διάφορες
στρατηγικές αναζήτησης για το module όταν ενεργοποιείται ο μηχανισμός
εισαγωγής. Αυτές οι στρατηγικές μπορούν να τροποποιηθούν και να
επεκταθούν μέσω διάφορων hooks που περιγράφονται στις παρακάτω
ενότητες.

Άλλαξε στην έκδοση 3.3: Το σύστημα εισαγωγής έχει ενημερωθεί ώστε να
υλοποιεί πλήρως τη δεύτερη φάση του **PEP 302**. Δεν υπάρχει πλέον
κανένας έμμεσος μηχανισμός εισαγωγής - ο πλήρης μηχανισμός εισαγωγής
εκτίθεται μέσω του "sys.meta_path". Επιπλέον, έχει υλοποιηθεί εγγενής
υποστήριξη για namespace packages (βλ. **PEP 420**).


5.1. "importlib"
================

Το module "importlib" παρέχει ένα πλούσιο API για αλληλεπίδραση με το
σύστημα εισαγωγής. Για παράδειγμα, η "importlib.import_module()"
παρέχει ένα προτεινόμενο, απλούστερο API από την ενσωματωμένη
"__import__()" για την ενεργοποίηση του μηχανισμού εισαγωγής.
Ανατρέξτε στην τεκμηρίωση της βιβλιοθήκης "importlib" για περισσότερες
λεπτομέρειες.


5.2. Πακέτα
===========

Η Python έχει μόνο έναν τύπο αντικειμένου module, και όλα τα modules
είναι αυτού του τύπου, ανεξάρτητα από το αν το module υλοποιείται σε
Python, C ή κάτι άλλο. Για να βοηθήσει στην οργάνωση των modules και
να παρέχει μια ιεραρχία ονοματοδοσίας, η Python διαθέτει την έννοια
των *packages*.

Μπορείτε να σκεφτείτε τα packages ως φακέλους σε ένα σύστημα αρχείων
και τα modules ως αρχεία μέσα σε φακέλους, αλλά μην πάρετε αυτή την
αναλογία κυριολεκτικά, καθώς packages και modules δεν είναι απαραίτητο
να προέρχονται από το σύστημα αρχείων. Για τους σκοπούς αυτής της
τεκμηρίωσης, θα χρησιμοποιήσουμε αυτή τη βολική αναλογία φακέλων και
αρχείων. Όπως οι φάκελοι του συστήματος αρχείων, τα packages
οργανώνονται ιεραρχικά, και μπορούν να περιέχουν subpackages, καθώς
και κανονικά modules.

Είναι σημαντικό να έχετε υπόψη ότι όλα τα packages είναι modules, αλλά
δεν είναι όλα τα modules packages. Με άλλα λόγια, τα packages είναι
απλώς ένας ειδικός τύπος module. Συγκεκριμένα, οποιοδήποτε module
περιέχει το χαρακτηριστικό "__path__" θεωρείται package.

Όλα τα modules έχουν ένα όνομα. Τα ονόματα των subpackages
διαχωρίζονται από το όνομα του γονικού package με τελεία, αντίστοιχα
με τη συνηθισμένη σύνταξη πρόσβασης σε χαρακτηριστικά στην Python.
Έτσι, μπορεί να έχετε ένα package με όνομα "email", το οποίο με τη
σειρά του έχει ένα subpackage με όνομα "email.mime" και ένα module
μέσα σε αυτό το subpackage με όνομα "email.mime.text".


5.2.1. Κανονικά πακέτα
----------------------

Η Python ορίζει δύο τύπους packages: τα *regular packages* και τα
*namespace packages*. Τα regular packages είναι τα παραδοσιακά
packages όπως υπήρχαν στην Python 3.2 και παλαιότερα. Ένα regular
package υλοποιείται συνήθως ως φάκελος που περιέχει ένα αρχείο
"__init__.py". Όταν εισάγεται ένα regular package, το αρχείο
"__init__.py" εκτελείται έμμεσα, και τα αντικείμενα που ορίζει
δεσμεύονται σε ονόματα στο namespace του package. Το "__init__.py"
μπορεί να περιέχει τον ίδιο κώδικα Python που μπορεί να περιέχει
οποιοδήποτε άλλο module, και η Python θα προσθέσει ορισμένα επιπλέον
χαρακτηριστικά στο module όταν εισάγεται.

Για παράδειγμα, η ακόλουθη διάταξη στο σύστημα αρχείων ορίζει ένα
package ανωτέρου επιπέδου "parent" με τρία subpackages:

   parent/
       __init__.py
       one/
           __init__.py
       two/
           __init__.py
       three/
           __init__.py

Η εισαγωγή του "parent.one" θα εκτελέσει έμμεσα τα
"parent/__init__.py" και "parent/one/__init__.py". Μετέπειτα εισαγωγές
του "parent.two" ή του "parent.three" θα εκτελέσουν τα
"parent/two/__init__.py" και "parent/three/__init__.py" αντίστοιχα.


5.2.2. Namespace πακέτα
-----------------------

Ένα namespace πακέτο είναι ένας συνδυασμός από διάφορα *portions*,
όπου κάθε portion συνεισφέρει ένα subpackage στο γονικό πακέτο. Τα
portions μπορεί να βρίσκονται σε διαφορετικές τοποθεσίες στο σύστημα
αρχείων. Μπορεί επίσης να βρίσκονται σε zip αρχεία, στο δίκτυο ή
οπουδήποτε αλλού αναζητά η Python κατά την εισαγωγή. Τα namespace
πακέτα μπορεί να αντιστοιχούν ή να μην αντιστοιχούν άμεσα σε
αντικείμενα στο σύστημα αρχείων· μπορεί να είναι «εικονικά» modules
που δεν έχουν συγκεκριμένη φυσική αναπαράσταση.

Τα namespace πακέτα δεν χρησιμοποιούν μια συνηθισμένη λίστα για το
χαρακτηριστικό "__path__". Αντί γι’ αυτό χρησιμοποιούν έναν
προσαρμοσμένο iterable τύπο, ο οποίος θα εκτελέσει αυτόματα μία νέα
αναζήτηση για portions του πακέτου στην επόμενη προσπάθεια εισαγωγής
μέσα σε αυτό το πακέτο, αν αλλάξει το path του γονικού πακέτου (ή το
"sys.path" για πακέτο ανωτέρου επιπέδου).

Με τα namespace πακέτα, δεν υπάρχει αρχείο "parent/__init__.py". Στην
πράξη, μπορεί να βρεθούν πολλοί φάκελοι "parent" κατά την αναζήτηση
εισαγωγής, όπου ο καθένας παρέχεται από διαφορετικό portion. Έτσι, το
"parent/one" ενδέχεται να μην βρίσκεται φυσικά δίπλα στο "parent/two".
Σε αυτή την περίπτωση, η Python θα δημιουργήσει ένα namespace πακέτο
για το πακέτο ανωτέρου επιπέδου "parent" κάθε φορά που γίνεται
εισαγωγή αυτού ή κάποιου από τα subpackages του.

Δείτε επίσης το **PEP 420** για την προδιαγραφή των namespace πακέτων.


5.3. Αναζήτηση
==============

Για να ξεκινήσει η αναζήτηση, η Python χρειάζεται το *πλήρως
αναγνωρισμένο* όνομα του module (ή του πακέτου, αλλά για τους σκοπούς
αυτής της συζήτησης η διαφορά δεν έχει σημασία) που εισάγεται. Αυτό το
όνομα μπορεί να προέρχεται από διάφορα ορίσματα της δήλωσης "import" ή
από τις παραμέτρους των συναρτήσεων "importlib.import_module()" ή
"__import__()".

Αυτό το όνομα θα χρησιμοποιηθεί σε διάφορες φάσεις της αναζήτησης
import και μπορεί να είναι μια διαδρομή με τελείες προς ένα submodule,
π.χ. "foo.bar.baz". Σε αυτή την περίπτωση, η Python πρώτα προσπαθεί να
εισάγει το "foo", μετά το "foo.bar" και τέλος το "foo.bar.baz". Αν
αποτύχει κάποιες από τις ενδιάμεσες εισαγωγές, γίνεται raise η
"ModuleNotFoundError".


5.3.1. Η προσωρινή μνήμη του module
-----------------------------------

Το πρώτο σημείο που ελέγχεται κατά την αναζήτηση εισαγωγής είναι το
"sys.modules". Αυτή η αντιστοίχιση λειτουργεί ως προσωρινή μνήμη όλων
των modules που έχουν εισαχθεί στο παρελθόν, συμπεριλαμβανομένων των
ενδιάμεσων διαδρομών. Έτσι, αν το "foo.bar.baz" είχε εισαχθεί
προηγουμένως, το "sys.modules" θα περιέχει εγγραφές για τα "foo",
"foo.bar" και "foo.bar.baz". Κάθε κλειδί θα έχει ως τιμή το αντίστοιχο
αντικείμενο module.

Κατά την εισαγωγή, το όνομα του module αναζητείται στο "sys.modules"
και, αν υπάρχει, η συσχετισμένη τιμή είναι το module που ικανοποιεί
την εισαγωγή και η διαδικασία ολοκληρώνεται. Ωστόσο, αν η τιμή είναι
"None", τότε γίνεται raise το "ModuleNotFoundError". Αν το όνομα του
module λείπει, η Python θα συνεχίσει να αναζητά το module.

Το "sys.modules" είναι εγγράψιμο. Η διαγραφή ενός κλειδιού μπορεί να
μην καταστρέψει το αντίστοιχο module (καθώς άλλα modules μπορεί να
κρατούν αναφορές σε αυτό), αλλά θα ακυρώσει την εγγραφή της προσωρινής
μνήμης για το module με το συγκεκριμένο όνομα, προκαλώντας την Python
να το αναζητήσει ξανά στην επόμενο εισαγωγή. Το κλειδί μπορεί επίσης
να οριστεί σε "None", αναγκάζοντας την επόμενο εισαγωγή του module να
καταλήξει σε "ModuleNotFoundError".

Προσοχή όμως: αν κρατήσετε μια αναφορά στο αντικείμενο module,
ακυρώσετε την εγγραφή του στην προσωρινή μνήμη του "sys.modules" και
μετά ξανακάνετε εισαγωγή το module με το ίδιο όνομα, τα δύο
αντικείμενα module *δεν* θα είναι τα ίδια. Αντίθετα, η
"importlib.reload()" θα επαναχρησιμοποιήσει το *ίδιο* αντικείμενο
module και απλώς θα επαναρχικοποιήσει τα περιεχόμενά του εκτελώντας
ξανά τον κώδικα του module.


5.3.2. Finders και loaders
--------------------------

Αν το module με το συγκεκριμένο όνομα δεν βρεθεί στο "sys.modules",
τότε ενεργοποιείται το πρωτόκολλο εισαγωγής της Python για να βρεθεί
και να φορτωθεί το module. Αυτό το πρωτόκολλο αποτελείται από δύο
εννοιολογικά αντικείμενα: *finders* και *loaders*. Ο ρόλος ενός finder
είναι να καθορίσει αν μπορεί να βρει το module με το συγκεκριμένο
όνομα, χρησιμοποιώντας όποια στρατηγική γνωρίζει. Αντικείμενα που
υλοποιούν και τις δύο αυτές διεπαφές αναφέρονται ως *importers* -
επιστρέφουν τον εαυτό τους όταν διαπιστώσουν ότι μπορούν να φορτώσουν
το ζητούμενο module.

Η Python περιλαμβάνει αρκετούς προεπιλεγμένους finders και importers.
Το πρώτο γνωρίζει πώς να εντοπίζει built-in modules, και το δεύτερο
πώς να εντοπίζει παγωμένα (frozen) modules. Ένας τρίτος προεπιλεγμένος
finder αναζητά modules σε ένα *import path*. Το *import path* είναι
μια λίστα από τοποθεσίες που μπορεί να είναι διαδρομές του συστήματος
αρχείων ή zip αρχεία. Μπορεί επίσης να επεκταθεί ώστε να αναζητά
οποιονδήποτε εντοπίσιμο πόρο, όπως αυτούς που προσδιορίζονται από
URLs.

Ο μηχανισμός εισαγωγής είναι επεκτάσιμος, επομένως μπορούν να
προστεθούν νέοι finders ώστε να επεκταθεί το εύρος και η κάλυψη της
αναζήτησης των modules.

Οι finders δεν φορτώνουν πραγματικά τα modules. Αν μπορούν να βρουν το
module με το συγκεκριμένο όνομα, επιστρέφουν ένα *module spec*, δηλαδή
μια ενθυλάκωση της πληροφορίας που σχετίζεται με την εισαγωγή του
module, την οποία ο μηχανισμός εισαγωγής χρησιμοποιεί στη συνέχεια για
να φορτώσει το module.

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

Άλλαξε στην έκδοση 3.4: Σε παλαιότερες εκδόσεις της Python, οι finders
επέστρεφαν απευθείας *loaders*, ενώ τώρα επιστρέφουν module specs που
*περιέχουν* loaders. Οι loaders εξακολουθούν να χρησιμοποιούνται κατά
την εισαγωγή, αλλά έχουν λιγότερες ευθύνες.


5.3.3. Εισαγωγή hooks
---------------------

Ο μηχανισμός εισαγωγής είναι σχεδιασμένος ώστε να είναι επεκτάσιμος· ο
βασικός μηχανισμός για αυτό είναι η *εισαγωγή hooks*. Υπάρχουν δύο
τύποι εισαγωγής hooks: *meta hooks* και *import path hooks*.

Τα meta hooks καλούνται στην αρχή της επεξεργασίας εισαγωγής, πριν
συμβεί οποιαδήποτε άλλη επεξεργασία εισαγωγής, εκτός από την αναζήτηση
στην προσωρινή μνήμη του "sys.modules". Αυτό επιτρέπει στα meta hooks
να υπερκαλύπτουν την επεξεργασία του "sys.path", τα παγωμένα modules ή
ακόμη και τα built-in modules. Τα meta hooks καταχωρίζονται
προσθέτοντας νέα αντικείμενα finder στο "sys.meta_path", όπως
περιγράφεται παρακάτω.

Τα import path hooks καλούνται ως μέρος της επεξεργασίας του
"sys.path" (ή του "package.__path__"), στο σημείο όπου συναντάται το
αντίστοιχο στοιχείο διαδρομής. Τα import path hooks καταχωρίζονται
προσθέτοντας νέα callables στο "sys.path_hooks", όπως περιγράφεται
παρακάτω.


5.3.4. Το meta path
-------------------

Όταν το module με το συγκεκριμένο όνομα δεν βρεθεί στο "sys.modules",
η Python στη συνέχεια το αναζητά στο "sys.meta_path", το οποίο
περιέχει μια λίστα από meta path finder αντικείμενα. Αυτοί οι finders
ερωτώνται με τη σειρά για να διαπιστωθεί αν γνωρίζουν πώς να
χειριστούν το module. Οι meta path finders πρέπει να υλοποιούν μια
μέθοδο με όνομα "find_spec()", η οποία λαμβάνει τρία ορίσματα: ένα
όνομα, ένα import path και (προαιρετικά) ένα target module. Ο meta
path finder μπορεί να χρησιμοποιήσει οποιαδήποτε στρατηγική θέλει για
να καθορίσει αν μπορεί να χειριστεί το module ή όχι.

Αν ο meta path finder γνωρίζει πώς να χειριστεί το module, επιστρέφει
ένα spec αντικείμενο. Αν δεν μπορεί να χειριστεί το module, επιστρέφει
"None". Αν η επεξεργασία του "sys.meta_path" φτάσει στο τέλος της
λίστας χωρίς να επιστραφεί spec, τότε γίνεται raise το
"ModuleNotFoundError". Οποιεσδήποτε άλλες εξαιρέσεις γίνονται raise
απλώς προωθούνται προς τα πάνω, τερματίζοντας τη διαδικασία εισαγωγής.

Η μέθοδος "find_spec()" των meta path finders καλείται με δύο ή τρία
ορίσματα. Το πρώτο είναι το πλήρως αναγνωρισμένο όνομα του module που
εισάγεται, π.χ. "foo.bar.baz". Το δεύτερο όρισμα είναι οι καταχωρήσεις
διαδρομής που θα χρησιμοποιηθούν για την αναζήτηση του module. Για
modules ανωτέρου επιπέδου, το δεύτερο όρισμα είναι "None", αλλά για
submodules ή subpackages, είναι η τιμή του χαρακτηριστικού "__path__"
του γονικού πακέτου. Αν δεν είναι δυνατή η πρόσβαση στο κατάλληλο
"__path__", γίνεται raise το "ModuleNotFoundError". Το τρίτο όρισμα
είναι ένα υπάρχον αντικείμενο module που θα αποτελέσει αργότερα τον
στόχο της φόρτωσης. Το σύστημα εισαγωγής περνά target module μόνο κατά
την επαναφόρτωση.

Το meta path μπορεί να διασχιστεί πολλές φορές για ένα μόνο αίτημα
εισαγωγής. Για παράδειγμα, υποθέτοντας ότι κανένα από τα εμπλεκόμενα
modules δεν έχει ήδη προστεθεί στην προσωρινή μνήμη, η εισαγωγή του
"foo.bar.baz" θα εκτελέσει πρώτα μία εισαγωγή ανωτέρου επιπέδου,
καλώντας το "mpf.find_spec("foo", None, None)" σε κάθε meta path
finder ("mpf"). Αφού εισαχθεί το "foo", το "foo.bar" θα εισαχθεί
διασχίζοντας το meta path για δεύτερη φορά, καλώντας την
"mpf.find_spec("foo.bar", foo.__path__, None)". Μόλις γίνει η εισαγωγή
του "foo.bar", η τελική διάσχιση θα καλέσει το
"mpf.find_spec("foo.bar.baz", foo.bar.__path__, None)".

Ορισμένοι meta path finders υποστηρίζουν μόνο εισαγωγές ανωτέρου
επιπέδου. Αυτοί οι importers θα επιστρέφουν πάντα "None" όταν ως
δεύτερο όρισμα περάσει οτιδήποτε άλλο εκτός από "None".

Το προεπιλεγμένο "sys.meta_path" της Python έχει τρεις meta path
finders: έναν που γνωρίζει πώς να εισάγει ενσωματωμένα modules, έναν
που γνωρίζει πώς να εισάγει παγωμένα modules, και έναν που γνωρίζει
πώς να εισάγει modules από ένα *import path* (δηλ. τον *path based
finder*).

Άλλαξε στην έκδοση 3.4: Η μέθοδος "find_spec()" των meta path finders
αντικατέστησε τη "find_module()", η οποία πλέον χαρακτηριστεί ως
απαρχαιωμένη. Αν και θα συνεχίσει να λειτουργεί χωρίς αλλαγές, ο
μηχανισμός εισαγωγής θα τη δοκιμάζει μόνο αν ο finder δεν υλοποιεί τη
"find_spec()".

Άλλαξε στην έκδοση 3.10: Η χρήση της "find_module()" από το σύστημα
εισαγωγής πλέον κάνει raise την "ImportWarning".

Άλλαξε στην έκδοση 3.12: Η "find_module()" έχει αφαιρεθεί.
Χρησιμοποιήστε την "find_spec()" στη θέση της.


5.4. Φόρτωση
============

Αν και όταν βρεθεί ένα module spec, ο μηχανισμός εισαγωγής θα το
χρησιμοποιήσει (και τον loader που περιέχει) για να φορτώσει το
module. Ακολουθεί μια προσέγγιση του τι συμβαίνει στο στάδιο φόρτωσης
του import:

   module = None
   if spec.loader is not None and hasattr(spec.loader, 'create_module'):
       # It is assumed 'exec_module' will also be defined on the loader.
       module = spec.loader.create_module(spec)
   if module is None:
       module = ModuleType(spec.name)
   # The import-related module attributes get set here:
   _init_module_attrs(spec, module)

   if spec.loader is None:
       # unsupported
       raise ImportError

   sys.modules[spec.name] = module
   try:
       spec.loader.exec_module(module)
   except BaseException:
       try:
           del sys.modules[spec.name]
       except KeyError:
           pass
       raise
   return sys.modules[spec.name]

Σημειώστε τις ακόλουθες λεπτομέρειες:

* Αν υπάρχει ήδη ένα αντικείμενο module με το συγκεκριμένο όνομα στο
  "sys.modules", το import θα το έχει ήδη επιστρέψει.

* Το module θα υπάρχει στο "sys.modules" πριν ο loader εκτελέσει τον
  κώδικα του module. Αυτό είναι κρίσιμο, επειδή ο κώδικας του module
  μπορεί (άμεσα ή έμμεσα) να εισάγει τον εαυτό του· η προσθήκη του στο
  "sys.modules" εκ των προτέρων αποτρέπει ατέρμονη αναδρομή στη
  χειρότερη περίπτωση και πολλαπλή φόρτωση στην καλύτερη.

* Αν η φόρτωση αποτύχει, το module που απέτυχε -- και μόνο αυτό --
  αφαιρείται από το "sys.modules". Οποιοδήποτε module βρίσκεται ήδη
  στην προσωρινή μνήμη του "sys.modules", καθώς και οποιοδήποτε module
  φορτώθηκε επιτυχώς ως παρενέργεια, πρέπει να παραμείνει στην
  προσωρινή μνήμη. Αυτό έρχεται σε αντίθεση με την επαναφόρτωση, όπου
  ακόμη και το module που αποτυγχάνει παραμένει στο "sys.modules".

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

* Η εκτέλεση του module είναι η κρίσιμη στιγμή της φόρτωσης κατά την
  οποία γεμίζει το namespace του module. Η εκτέλεση ανατίθεται εξ
  ολοκλήρου στον loader, ο οποίος αποφασίζει τι θα συμπληρωθεί και με
  ποιον τρόπο.

* Το module που δημιουργείται κατά τη φόρτωση και περνά στη
  exec_module() ενδέχεται να μην είναι αυτό που επιστρέφεται στο τέλος
  του import [2].

Άλλαξε στην έκδοση 3.4: The import system has taken over the
boilerplate responsibilities of loaders.  These were previously
performed by the "importlib.abc.Loader.load_module" method.

Άλλαξε στην έκδοση 3.15: The "load_module" method is no longer used.


5.4.1. Loaders
--------------

Οι module loaders παρέχουν την κρίσιμη λειτουργία της φόρτωσης: την
εκτέλεση του module. Ο μηχανισμός εισαγωγής καλεί τη μέθοδο
"importlib.abc.Loader.exec_module()" με ένα μόνο όρισμα, το
αντικείμενο module που θα εκτελεστεί. Οποιαδήποτε τιμή επιστρέφει η
"exec_module()" αγνοείται.

Οι loaders πρέπει να ικανοποιούν τις ακόλουθες απαιτήσεις:

* Αν το module είναι Python module (σε αντίθεση με ένα ενσωματωμένο
  module ή δυναμικά φορτωμένη επέκταση), ο loader θα πρέπει να
  εκτελέσει τον κώδικα του module στο καθολικό namespace του module
  ("module.__dict__").

* Αν ο loader δεν μπορεί να εκτελέσει το module, θα πρέπει να κάνει
  raise το "ImportError", αν και οποιαδήποτε άλλη εξαίρεση γίνει raise
  κατά τη "exec_module()" θα προωθηθεί προς τα πάνω.

Σε πολλές περιπτώσεις, ο finder και ο loader μπορεί να είναι το ίδιο
αντικείμενο· σε αυτές τις περιπτώσεις, η μέθοδος "find_spec()" απλώς
θα επιστρέψει ένα spec με τον loader ορισμένο σε "self".

Οι module loaders μπορούν, προαιρετικά, να δημιουργούν το αντικείμενο
module κατά τη φόρτωση υλοποιώντας μια μέθοδο "create_module()".
Παίρνει ένα όρισμα, το module spec, και επιστρέφει το νέο αντικείμενο
module που θα χρησιμοποιηθεί κατά τη φόρτωση. Η "create_module()" δεν
χρειάζεται να ορίσει χαρακτηριστικά στο αντικείμενο module. Αν η
μέθοδος επιστρέψει "None", ο μηχανισμός εισαγωγής θα δημιουργήσει ο
ίδιος το νέο module.

Added in version 3.4: Η μέθοδος "create_module()" των loaders.

Άλλαξε στην έκδοση 3.4: The "importlib.abc.Loader.load_module" method
was replaced by "exec_module()" and the import machinery assumed all
the boilerplate responsibilities of loading.Για συμβατότητα με
υπάρχοντες loaders, ο μηχανισμός εισαγωγής θα χρησιμοποιήσει τη μέθοδο
"load_module()" αν υπάρχει και ο loader δεν υλοποιεί την
"exec_module()". Ωστόσο, η "load_module()" είναι απαρχαιωμένη και οι
loaders θα πρέπει να υλοποιούν την "exec_module()".Η μέθοδος
"load_module()" πρέπει να υλοποιεί όλη την τυποποιημένη
λειτουργικότητα φόρτωσης που περιγράφεται παραπάνω, επιπλέον της
εκτέλεσης του module. Ισχύουν οι ίδιοι περιορισμοί, με κάποιες
επιπλέον διευκρινίσεις:

* Αν υπάρχει ήδη αντικείμενο module με το συγκεκριμένο όνομα στο
  "sys.modules", ο loader πρέπει να χρησιμοποιήσει αυτό το υπάρχον
  module. (Διαφορετικά, η "importlib.reload()" δεν θα λειτουργεί
  σωστά.) Αν το module με το συγκεκριμένο όνομα δεν υπάρχει στο
  "sys.modules", ο loader πρέπει να δημιουργήσει νέο αντικείμενο
  module και να το προσθέσει στο "sys.modules".

* Το module *πρέπει* να υπάρχει στο "sys.modules" πριν ο loader
  εκτελέσει τον κώδικα του module, ώστε να αποτραπεί ατέρμονη αναδρομή
  ή πολλαπλή φόρτωση.

* Αν η φόρτωση αποτύχει, ο loader πρέπει να αφαιρέσει οποιαδήποτε
  modules έχει εισάγει στο "sys.modules", αλλά πρέπει να αφαιρέσει
  **μόνο** το/τα module(s) που απέτυχαν, και μόνο αν ο loader έχει
  φορτώσει τα module(s) αυτά ρητά.

Άλλαξε στην έκδοση 3.5: Γίνεται raise η "DeprecationWarning" όταν
ορίζεται η "exec_module()" αλλά δεν ορίζεται η "create_module()".

Άλλαξε στην έκδοση 3.6: Γίνεται raise το "ImportError" όταν ορίζεται η
"exec_module()" αλλά δεν ορίζεται η "create_module()".

Άλλαξε στην έκδοση 3.10: Η χρήση της "load_module()" θα κάνει raise
την "ImportWarning".


5.4.2. Submodules
-----------------

Όταν ένα submodule φορτώνεται με οποιονδήποτε μηχανισμό (π.χ.
"importlib" APIs, τις δηλώσεις "import" ή "import-from", ή την
ενσωματωμένη "__import__()"), δημιουργείται μια δέσμευση στο namespace
του γονικού module προς το αντικείμενο του submodule. Για παράδειγμα,
αν το πακέτο "spam" έχει submodule "foo", μετά την εισαγωγή του
"spam.foo", το "spam" θα έχει ένα χαρακτηριστικό "foo" που είναι
δεσμευμένο στο submodule. Ας πούμε ότι έχετε την ακόλουθη δομή
φακέλων:

   spam/
       __init__.py
       foo.py

και το "spam/__init__.py" περιέχει την ακόλουθη γραμμή:

   from .foo import Foo

τότε, η εκτέλεση των παρακάτω δημιουργεί δεσμεύσεις ονομάτων για τα
"foo" και "Foo" στο module "spam":

   >>> import spam
   >>> spam.foo
   <module 'spam.foo' from '/tmp/imports/spam/foo.py'>
   >>> spam.Foo
   <class 'spam.foo.Foo'>

Με δεδομένους τους γνώριμους κανόνες δέσμευσης ονομάτων της Python,
αυτό μπορεί να φαίνεται παράξενο, αλλά στην πραγματικότητα είναι
θεμελιώδες χαρακτηριστικό του συστήματος εισαγωγής. Η αμετάβλητη
συνθήκη είναι ότι, αν έχετε "sys.modules['spam']" και
"sys.modules['spam.foo']" (όπως θα είχατε μετά την παραπάνω εισαγωγή),
το δεύτερο πρέπει να εμφανίζεται ως το χαρακτηριστικό "foo" του
πρώτου.


5.4.3. Module specs
-------------------

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

Η χρήση ενός spec κατά την εισαγωγή επιτρέπει τη μεταφορά κατάστασης
μεταξύ των συστατικών του συστήματος εισαγωγής, π.χ. μεταξύ του finder
που δημιουργεί το module spec και του loader που το εκτελεί. Το
σημαντικότερο είναι ότι επιτρέπει στον μηχανισμό εισαγωγής να εκτελεί
τις τυποποιημένες λειτουργίες φόρτωσης, ενώ χωρίς module spec αυτή η
ευθύνη ανήκε στον loader.

Το spec του module εκτίθεται ως "module.__spec__". Η κατάλληλη ρύθμιση
του "__spec__" ισχύει εξίσου για modules που αρχικοποιούνται κατά την
εκκίνηση του διερμηνέα. Η μόνη εξαίρεση είναι το "__main__", όπου το
"__spec__" τίθεται σε None σε ορισμένες περιπτώσεις.

Δείτε την "ModuleSpec" για λεπτομέρειες σχετικά με τα περιεχόμενα του
module spec.

Added in version 3.4.


5.4.4. Χαρακτηριστικά __path__ στα modules
------------------------------------------

Το χαρακτηριστικό "__path__" θα πρέπει να είναι μια (ενδεχομένως κενή)
*sequence* από συμβολοσειρές που απαριθμούν τις τοποθεσίες όπου θα
βρεθούν τα submodules του πακέτου. Εξ ορισμού, αν ένα module έχει
χαρακτηριστικό "__path__", τότε είναι *package*.

Το χαρακτηριστικό "__path__" ενός πακέτου χρησιμοποιείται κατά την
εισαγωγή των subpackages του. Μέσα στον μηχανισμό εισαγωγής,
λειτουργεί παρόμοια με το "sys.path", δηλαδή παρέχει μια λίστα
τοποθεσιών στις οποίες αναζητούνται modules κατά την εισαγωγή. Ωστόσο,
το "__path__" είναι συνήθως πολύ πιο περιορισμένο από το "sys.path".

Οι ίδιοι κανόνες που ισχύουν για το "sys.path" ισχύουν επίσης και για
το "__path__" ενός πακέτου. Τα "sys.path_hooks" (που περιγράφονται
παρακάτω) λαμβάνονται υπόψη κατά τη διάσχιση του "__path__" ενός
πακέτου.

Το αρχείο "__init__.py" ενός πακέτου μπορεί να ορίζει ή να τροποποιεί
το χαρακτηριστικό "__path__" του πακέτου, και αυτός ήταν συνήθως ο
τρόπος με τον οποίο υλοποιούνταν τα namespace πακέτα πριν το **PEP
420**. Με την υιοθέτηση του **PEP 420**, τα namespace πακέτα δεν
χρειάζεται πλέον να παρέχουν αρχεία "__init__.py" που περιέχουν μόνο
κώδικα χειρισμού του "__path__"· ο μηχανισμός εισαγωγής ορίζει
αυτόματα σωστά το "__path__" για το namespace πακέτο.


5.4.5. Reprs των modules
------------------------

Από προεπιλογή, όλα τα modules έχουν ένα χρησιμοποιήσιμο repr· ωστόσο,
ανάλογα με τα χαρακτηριστικά που ορίζονται παραπάνω και στο spec του
module, μπορείτε να ελέγχετε πιο ρητά το repr των αντικειμένων module.

Αν το module έχει spec ("__spec__"), ο μηχανισμός εισαγωγής θα
προσπαθήσει να παράγει repr από αυτό. Αν αυτό αποτύχει ή δεν υπάρχει
spec, το σύστημα εισαγωγής θα δημιουργήσει ένα προεπιλεγμένο repr
χρησιμοποιώντας όποια πληροφορία είναι διαθέσιμη στο module. Θα
προσπαθήσει να χρησιμοποιήσει τα "module.__name__", "module.__file__"
και "module.__loader__" ως είσοδο για το repr, με προεπιλογές για
οποιαδήποτε πληροφορία λείπει.

Ακολουθούν οι ακριβείς κανόνες που χρησιμοποιούνται:

* Αν το module έχει χαρακτηριστικό "__spec__", οι πληροφορίες στο spec
  χρησιμοποιούνται για να παραχθεί το repr. Λαμβάνονται υπόψη τα
  χαρακτηριστικά "name", "loader", "origin" και "has_location".

* Αν το module έχει χαρακτηριστικό "__file__", αυτό χρησιμοποιείται ως
  μέρος του repr του module.

* Αν το module δεν έχει "__file__" αλλά έχει "__loader__" που δεν
  είναι "None", τότε το repr του loader χρησιμοποιείται ως μέρος του
  repr του module.

* Διαφορετικά, χρησιμοποιείται απλώς το "__name__" του module στο
  repr.

Άλλαξε στην έκδοση 3.12: Η χρήση της "module_repr()", η οποία ήταν
δηλωμένη ως απαρχαιωμένη από την Python 3.4, αφαιρέθηκε στην Python
3.12 και δεν καλείται πλέον κατά την επίλυση του repr ενός module.


5.4.6. Ακύρωση cached bytecode
------------------------------

Πριν η Python φορτώσει cached bytecode από ένα αρχείο ".pyc", ελέγχει
αν η προσωρινή μνήμη είναι ενημερωμένη σε σχέση με το πηγαίο αρχείο
".py". Από προεπιλογή, η Python το κάνει αυτό αποθηκεύοντας τη χρονική
σήμανση της τελευταίας τροποποίησης και το μέγεθος του πηγαίου αρχείου
στο αρχείο προσωρινής μνήμης κατά την εγγραφή του. Κατά το χρόνο
εκτέλεσης, το σύστημα εισαγωγής επικυρώνει το αρχείο προσωρινής μνήμης
ελέγχοντας τα αποθηκευμένα μεταδεδομένα του αρχείου προσωρινής μνήμης
σε σχέση με τα μεταδεδομένα του πηγαίου αρχείου.

Η Python υποστηρίζει επίσης αρχεία προσωρινής μνήμης βασισμένα σε
hash, τα οποία αποθηκεύουν ένα hash του περιεχομένου του πηγαίου
αρχείου αντί για τα μεταδεδομένα του. Υπάρχουν δύο παραλλαγές των
".pyc" αρχείων βασισμένων σε hash: checked και unchecked. Για τα
checked ".pyc" αρχεία που βασίζονται σε hash, η Python επικυρώνει το
αρχείο προσωρινής μνήμης δημιουργώντας hash του πηγαίου αρχείου και
συγκρίνοντας το αποτέλεσμα με το hash στο αρχείο προσωρινής μνήμης. Αν
ένα checked αρχείο προσωρινής μνήμης βασισμένο σε hash βρεθεί μη
έγκυρο, η Python το αναδημιουργεί και γράφει ένα νέο checked αρχείο
προσωρινής μνήμης βασισμένο σε hash. Για τα unchecked ".pyc" αρχεία
βασισμένα σε hash, η Python απλώς θεωρεί ότι το αρχείο προσωρινής
μνήμης είναι έγκυρο αν υπάρχει. Η συμπεριφορά επικύρωσης των  ".pyc"
αρχείων βασισμένων σε hash μπορεί να υπερκαλυφθεί με τη σημαία "--
check-hash-based-pycs".

Άλλαξε στην έκδοση 3.7: Προστέθηκαν``.pyc`` αρχεία βασισμένα σε hash.
Προηγουμένως, η Python υποστήριζε μόνο ακύρωση των bytecode caches με
βάση χρονικές σημάνσεις.


5.5. Finder βασισμένος στη διαδρομή
===================================

Όπως αναφέρθηκε προηγουμένως, η Python συνοδεύεται από αρκετούς
προεπιλεγμένους meta path finders. Ένας από αυτούς, που ονομάζεται
*path based finder* ("PathFinder"), αναζητά σε ένα *import path*, το
οποίο περιέχει μια λίστα από *path entries*. Κάθε path entry ονομάζει
μια τοποθεσία όπου θα αναζητηθούν modules.

Ο ίδιος ο path based finder δεν «γνωρίζει» πώς να εισάγει οτιδήποτε.
Αντ’ αυτού, διασχίζει τις επιμέρους καταχωρήσεις διαδρομής,
συσχετίζοντας το καθένα με έναν finder καταχώρησης διαδρομής που
γνωρίζει πώς να χειριστεί αυτόν τον συγκεκριμένο τύπο διαδρομής.

Το προεπιλεγμένο σύνολο finders καταχώρησης διαδρομής υλοποιεί όλη τη
σημασιολογία για την εύρεση των modules στο σύστημα αρχείων,
χειριζόμενο ειδικούς τύπους αρχείων όπως πηγαίο κώδικα Python (αρχεία
".py"), byte code Python (αρχεία ".pyc") και κοινόχρηστες βιβλιοθήκες
(π.χ. αρχεία ".so"). Όταν υποστηρίζεται από το module "zipimport" της
τυπικής βιβλιοθήκης, οι προεπιλεγμένοι finders καταχώρησης διαδρομής
χειρίζονται επίσης τη φόρτωση όλων αυτών των τύπων αρχείων (εκτός από
κοινόχρηστες βιβλιοθήκες) από zipfiles.

Οι καταχωρήσεις διαδρομής δεν χρειάζεται να περιορίζονται σε
τοποθεσίες του συστήματος αρχείων. Μπορούν να αναφέρονται σε URLs, σε
ερωτήματα βάσεων δεδομένων ή σε οποιαδήποτε άλλη τοποθεσία που μπορεί
να εκφραστεί ως συμβολοσειρά.

Ο path based finder παρέχει επιπλέον hooks και πρωτόκολλα ώστε να
μπορείτε να επεκτείνετε και να προσαρμόσετε τους τύπους των
καταχωρήσεων διαδρομής που μπορούν να αναζητηθούν. Για παράδειγμα, αν
θέλατε να υποστηρίξετε καταχωρήσεις διαδρομής ως δικτυακά URLs, θα
μπορούσατε να γράψετε ένα hook που υλοποιεί HTTP σημασιολογία για να
βρίσκει modules στο διαδίκτυο. Αυτό το hook (ένα callable) θα
επέστρεφε έναν *path entry finder* που υποστηρίζει το πρωτόκολλο που
περιγράφεται παρακάτω, και στη συνέχεια θα χρησιμοποιούνταν για να
ληφθεί ένας loader για το module από το διαδίκτυο.

Μια προειδοποίηση: αυτή η ενότητα και η προηγούμενη χρησιμοποιούν και
οι δύο τον όρο *finder*, διακρίνοντάς τους με τους όρους *meta path
finder* και *path entry finder*. Αυτοί οι δύο τύποι finders είναι πολύ
παρόμοιοι, υποστηρίζουν παρόμοια πρωτόκολλα και λειτουργούν με
παρόμοιο τρόπο κατά τη διαδικασία εισαγωγής, αλλά είναι σημαντικό να
θυμάστε ότι έχουν λεπτές διαφορές. Συγκεκριμένα, οι meta path finders
λειτουργούν στην αρχή της διαδικασίας εισαγωγής, όπως καθορίζεται από
τη διάσχιση του "sys.meta_path".

Αντίθετα, οι path entry finders είναι κατά κάποιον τρόπο μια
λεπτομέρεια υλοποίησης του path based finder και, στην πραγματικότητα,
αν ο path based finder αφαιρούνταν από το "sys.meta_path", δεν θα
ενεργοποιούνταν καθόλου η σημασιολογία των finders καταχώρησης
διαδρομής.


5.5.1. Finders καταχώρησης διαδρομής (Path entry finders)
---------------------------------------------------------

Ο *path based finder* είναι υπεύθυνος για την εύρεση και φόρτωση
Python modules και πακέτων των οποίων η τοποθεσία καθορίζεται με μία
συμβολοσειρά *path entry*. Οι περισσότερες καταχωρήσεις διαδρομής
(path entries) δηλώνουν τοποθεσίες στο σύστημα αρχείων, αλλά δεν
περιορίζονται απαραίτητα σε αυτό.

Ως ένα meta path finder, ο *path based finder* υλοποιεί το πρωτόκολλο
"find_spec()" που περιγράφηκε προηγουμένως, ωστόσο εκθέτει επιπλέον
hooks που μπορούν να χρησιμοποιηθούν για να προσαρμόσετε το πώς
βρίσκονται και φορτώνονται τα modules από το *import path*.

Τρεις μεταβλητές χρησιμοποιούνται από τον *path based finder*:
"sys.path", "sys.path_hooks" και "sys.path_importer_cache". Επίσης
χρησιμοποιούνται τα χαρακτηριστικά "__path__" σε αντικείμενα πακέτου.
Αυτά παρέχουν επιπλέον τρόπους παραμετροποίησης του μηχανισμού
εισαγωγής.

Το "sys.path" περιέχει μια λίστα από συμβολοσειρές που παρέχουν
τοποθεσίες αναζήτησης για modules και πακέτα. Αρχικοποιείται από τη
μεταβλητή περιβάλλοντος "PYTHONPATH" και από διάφορες άλλες
προεπιλογές που εξαρτώνται από την εγκατάσταση και την υλοποίηση. Οι
εγγραφές στο "sys.path" μπορεί να είναι φάκελοι στο σύστημα αρχείων,
zip αρχεία και ενδεχομένως άλλες «τοποθεσίες» (βλ. το module "site")
στις οποίες πρέπει να γίνει αναζήτηση για modules, όπως URLs ή
ερωτήματα βάσεων δεδομένων. Στο "sys.path" θα πρέπει να υπάρχουν μόνο
συμβολοσειρές· όλοι οι άλλοι τύποι δεδομένων αγνοούνται.

Ο *path based finder* είναι *meta path finder*, επομένως ο μηχανισμός
εισαγωγής ξεκινά την αναζήτηση στο *import path* καλώντας τη μέθοδο
"find_spec()" του path based finder, όπως περιγράφηκε προηγουμένως.
Όταν δοθεί το όρισμα "path" στη "find_spec()", αυτό θα είναι μια λίστα
από συμβολοσειρές διαδρομής προς διάσχιση - συνήθως το χαρακτηριστικό
"__path__" ενός πακέτου για εισαγωγή μέσα σε εκείνο το πακέτο. Αν το
όρισμα "path" είναι "None", αυτό υποδηλώνει εισαγωγή ανωτέρου επιπέδου
και χρησιμοποιείται το "sys.path".

Ο path based finder επαναλαμβάνεται πάνω σε κάθε εγγραφή στη διαδρομή
αναζήτησης και, για καθεμία, αναζητά έναν κατάλληλο *path entry
finder* ("PathEntryFinder") για τη συγκεκριμένη καταχώρηση διαδρομής.
Επειδή αυτό μπορεί να είναι δαπανηρή λειτουργία (π.χ. μπορεί να
υπάρχει overhead από κλήσεις "stat()" για αυτή την αναζήτηση), ο path
based finder διατηρεί μια προσωρινή μνήμη που αντιστοιχίζει
καταχωρήσεις διαδρομής σε path entry finders. Αυτή η προσωρινή μνήμη
διατηρείται στο "sys.path_importer_cache" (παρά το όνομα, αυτή η
προσωρινή μνήμη αποθηκεύει αντικείμενα finder και όχι μόνο *importer*
αντικείμενα). Με αυτόν τον τρόπο, η ακριβή αναζήτηση για τον *path
entry* μιας συγκεκριμένης τοποθεσίας *path entry finder* χρειάζεται να
γίνει μόνο μία φορά. Ο κώδικας του χρήστη μπορεί να αφαιρεί εγγραφές
από το "sys.path_importer_cache", αναγκάζοντας τον path based finder
να εκτελέσει ξανά την αναζήτηση.

Αν η καταχώρηση διαδρομής δεν υπάρχει στην προσωρινή μνήμη, ο path
based finder επαναλαμβάνεται πάνω σε κάθε callable στο
"sys.path_hooks". Κάθε *path entry hook* σε αυτή τη λίστα καλείται με
ένα μόνο όρισμα: την καταχώρηση διαδρομής που θα αναζητηθεί. Αυτό το
callable μπορεί είτε να επιστρέψει έναν *path entry finder* που μπορεί
να χειριστεί την καταχώρηση διαδρομής, είτε να κάνει raise ένα
"ImportError". Το "ImportError" χρησιμοποιείται από τον path based
finder για να δηλώσει ότι το hook δεν μπορεί να βρει *path entry
finder* για το συγκεκριμένο *path entry*. Η εξαίρεση αγνοείται και η
επανάληψη στο *import path* συνεχίζεται. Το hook θα πρέπει να αναμένει
είτε συμβολοσειρά είτε αντικείμενο bytes· η κωδικοποίηση των bytes
αντικειμένων επαφίεται στο hook (π.χ. μπορεί να είναι μία κωδικοποίηση
του συστήματος αρχείων, UTF-8 ή κάτι άλλο), και αν το hook δεν μπορεί
να κάνει αποκωδικοποίηση το όρισμα, θα πρέπει να κάνει raise ένα
"ImportError".

Αν η επανάληψη στο "sys.path_hooks" ολοκληρωθεί χωρίς να επιστραφεί
*path entry finder*, τότε η μέθοδος "find_spec()" του path based
finder θα αποθηκεύσει "None" στο "sys.path_importer_cache" (για να
δηλώσει ότι δεν υπάρχει finder για αυτή την καταχώρηση διαδρομής) και
θα επιστρέψει "None", δηλώνοντας ότι αυτός ο *meta path finder* δεν
μπόρεσε να βρει το module.

Αν *επιστραφεί* ένας *path entry finder* από κάποιο από τα *path entry
hook* callables στο "sys.path_hooks", τότε χρησιμοποιείται το ακόλουθο
πρωτόκολλο για να ζητηθεί από τον finder ένα module spec, το οποίο στη
συνέχεια χρησιμοποιείται κατά τη φόρτωση του module.

Ο τρέχων κατάλογος εργασίας -- που δηλώνεται με κενή συμβολοσειρά --
αντιμετωπίζεται λίγο διαφορετικά από τις άλλες εγγραφές στο
"sys.path". Πρώτον, αν δεν μπορεί να προσδιοριστεί ή αν διαπιστωθεί
ότι δεν υπάρχει, δεν αποθηκεύεται τιμή στο "sys.path_importer_cache".
Δεύτερον, η τιμή για τον τρέχοντα κατάλογο εργασίας αναζητείται εκ
νέου για κάθε αναζήτηση module. Τρίτον, η διαδρομή που χρησιμοποιείται
για το "sys.path_importer_cache" και επιστρέφεται από τη
"importlib.machinery.PathFinder.find_spec()" θα είναι ο πραγματικός
τρέχων κατάλογος εργασίας και όχι η κενή συμβολοσειρά.


5.5.2. Πρωτόκολλο path entry finder
-----------------------------------

Για να υποστηρίζεται η εισαγωγή των modules και των αρχικοποιημένων
πακέτων, καθώς και για τη συνεισφορά portions σε namespace πακέτα, οι
path entry finders πρέπει να υλοποιούν τη μέθοδο "find_spec()".

Η "find_spec()" παίρνει δύο ορίσματα: το πλήρως αναγνωρισμένο όνομα
του module που εισάγεται και το (προαιρετικό) target module. Η
"find_spec()" επιστρέφει ένα πλήρως συμπληρωμένο spec για το module.
Αυτό το spec θα έχει πάντα ορισμένο το "loader" (με μία εξαίρεση).

Για να δηλώσει στον μηχανισμό εισαγωγής ότι το spec αναπαριστά ένα
namespace *portion*, ο path entry finder ορίζει το
"submodule_search_locations" σε μια λίστα που περιέχει το portion.

Άλλαξε στην έκδοση 3.4: Η "find_spec()" αντικατέστησε τις
"find_loader()" και "find_module()", οι οποίες έχουν πλέον
χαρακτηριστεί ως απαρχαιωμένες, αλλά θα χρησιμοποιηθούν αν δεν
ορίζεται η "find_spec()".Παλαιότεροι path entry finders μπορεί να
υλοποιούν μία από αυτές τις δύο απαρχαιωμένες μεθόδους αντί για την
"find_spec()". Οι μέθοδοι αυτές εξακολουθούν να γίνονται δεκτές για
λόγους συμβατότητας προς τα πίσω. Ωστόσο, αν ο path entry finder
υλοποιεί "find_spec()", οι παλιές μέθοδοι αγνοούνται.Η "find_loader()"
παίρνει ένα όρισμα, το πλήρως αναγνωρισμένο όνομα του module που
εισάγεται. Η "find_loader()" επιστρέφει ένα tuple όπου το πρώτο
στοιχείο είναι ο loader και το δεύτερο στοιχείο είναι ένα namespace
*portion*.Για συμβατότητα προς τα πίσω με άλλες υλοποιήσεις του
πρωτοκόλλου εισαγωγής, πολλοί path entry finders υποστηρίζουν επίσης
την ίδια παραδοσιακή μέθοδο "find_module()" που υποστηρίζουν και οι
meta path finders. Ωστόσο, οι μέθοδοι "find_module()" των path entry
finders δεν καλούνται ποτέ με όρισμα "path" (αναμένεται να έχουν
καταγράψει την κατάλληλη πληροφορία διαδρομής από την αρχική κλήση του
path hook).Η μέθοδος "find_module()" στους path entry finders έχει
χαρακτηριστεί ως απαρχαιωμένη, καθώς δεν επιτρέπει στον path entry
finder να συνεισφέρει portions σε namespace πακέτα. Αν ένας path entry
finder διαθέτει και "find_loader()" και "find_module()", το σύστημα
εισαγωγής θα καλεί πάντα την "find_loader()" αντί για την
"find_module()".

Άλλαξε στην έκδοση 3.10: Οι κλήσεις των "find_module()" και
"find_loader()" από το σύστημα εισαγωγής θα κάνουν raise μία
"ImportWarning".

Άλλαξε στην έκδοση 3.12: Οι "find_module()" και "find_loader()" έχουν
αφαιρεθεί.


5.6. Αντικατάσταση του τυπικού συστήματος εισαγωγής
===================================================

Ο πιο αξιόπιστος μηχανισμός για την αντικατάσταση ολόκληρου του
συστήματος εισαγωγής είναι η διαγραφή των προεπιλεγμένων περιεχομένων
του "sys.meta_path", αντικαθιστώντας τα πλήρως με ένα προσαρμοσμένο
hook μετά-διαδρομής.

If it is acceptable to only alter the behaviour of import statements
without affecting other APIs that access the import system, then
replacing the builtin "__import__()" function may be sufficient.

Για να αποτρέψετε επιλεκτικά την εισαγωγή ορισμένων modules από ένα
hook νωρίς στη μεταδιαδρομή (αντί να απενεργοποιήσετε πλήρως το τυπικό
σύστημα εισαγωγής), αρκεί να κάνετε raise το "ModuleNotFoundError"
απευθείας από τη "find_spec()" αντί να επιστρέψετε "None". Το "None"
δηλώνει ότι η αναζήτηση στη μεταδιαδρομή πρέπει να συνεχιστεί, ενώ η
υποβολή αιτήματος εξαίρεσης την τερματίζει άμεσα.


5.7. Σχετικές εισαγωγές σε πακέτα
=================================

Οι σχετικές εισαγωγές χρησιμοποιούν τελείες στην αρχή. Μια και μόνο
τελεία στην αρχή δηλώνει σχετική εισαγωγή που ξεκινά από το τρέχον
πακέτο. Δύο ή περισσότερες τελείες δηλώνουν σχετική εισαγωγή προς
το/τα γονικό/ά του τρέχοντος πακέτου, ένα επίπεδο ανά τελεία μετά την
πρώτη. Για παράδειγμα, με την ακόλουθη διάταξη πακέτου:

   package/
       __init__.py
       subpackage1/
           __init__.py
           moduleX.py
           moduleY.py
       subpackage2/
           __init__.py
           moduleZ.py
       moduleA.py

Είτε στο "subpackage1/moduleX.py" είτε στο "subpackage1/__init__.py",
τα παρακάτω είναι έγκυρες σχετικές εισαγωγές:

   from .moduleY import spam
   from .moduleY import spam as ham
   from . import moduleY
   from ..subpackage1 import moduleY
   from ..subpackage2.moduleZ import eggs
   from ..moduleA import foo

Οι απόλυτες εισαγωγές μπορούν να χρησιμοποιούν είτε τη σύνταξη "import
<>" είτε τη "from <> import <>", αλλά οι σχετικές εισαγωγές μπορούν να
χρησιμοποιούν μόνο τη δεύτερη μορφή· ο λόγος είναι ότι:

   import XXX.YYY.ZZZ

θα πρέπει να εκθέτει το "XXX.YYY.ZZZ" ως χρησιμοποιήσιμη έκφραση, αλλά
το .moduleY δεν είναι έγκυρη έκφραση.


5.8. Ειδικές περιπτώσεις για το __main__
========================================

Το module "__main__" είναι μια ειδική περίπτωση σε σχέση με το σύστημα
εισαγωγής της Python. Όπως σημειώνεται αλλού, το "__main__"
αρχικοποιείται απευθείας κατά την εκκίνηση του διερμηνέα, όπως και τα
"sys" και "builtins". Ωστόσο, σε αντίθεση με αυτά τα δύο, δεν
χαρακτηρίζεται αυστηρά ως ενσωματωμένο module. Αυτό συμβαίνει επειδή ο
τρόπος με τον οποίο αρχικοποιείται το "__main__" εξαρτάται από τις
σημαίες και άλλες επιλογές με τις οποίες καλείται ο διερμηνέας.


5.8.1. __main__.__spec__
------------------------

Ανάλογα με το πώς αρχικοποιείται το "__main__", το "__main__.__spec__"
ορίζεται κατάλληλα ή σε "None".

Όταν η Python ξεκινά με την επιλογή "-m", το "__spec__" ορίζεται στο
module spec του αντίστοιχου module ή πακέτου. Το "__spec__"
συμπληρώνεται επίσης όταν το module "__main__" φορτώνεται ως μέρος της
εκτέλεσης ενός καταλόγου, zipfile ή άλλης εγγραφής του "sys.path".

Στις υπόλοιπες περιπτώσεις το "__main__.__spec__" ορίζεται σε "None",
καθώς ο κώδικας που χρησιμοποιείται για να γεμίσει το "__main__" δεν
αντιστοιχεί άμεσα σε εισαγόμενο module:

* διαδραστική προτροπή

* επιλογή "-c"

* εκτέλεση από stdin

* εκτέλεση απευθείας από το αρχείο πηγαίου κώδικα ή bytecode

Σημειώστε ότι στην τελευταία περίπτωση το "__main__.__spec__" είναι
πάντα "None", *ακόμη κι αν* το αρχείο θα μπορούσε τεχνικά να εισαχθεί
απευθείας ως module. Χρησιμοποιήστε το switch "-m" αν χρειάζεστε
έγκυρα module μεταδεδομένων στο "__main__".

Σημειώστε επίσης ότι ακόμη κι όταν το "__main__" αντιστοιχεί σε
εισαγόμενα module και το "__main__.__spec__" ορίζεται ανάλογα,
εξακολουθούν να θεωρούνται *διακριτά* modules. Αυτό οφείλεται στο ότι
μπλοκ κώδικα που προστατεύονται από ελέγχους "if __name__ ==
"__main__":" εκτελούνται μόνο όταν το module χρησιμοποιείται για να
γεμίσει το namespace του "__main__", και όχι κατά την κανονική
εισαγωγή.


5.9. Αναφορές
=============

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

Η αρχική προδιαγραφή για το "sys.meta_path" ήταν το **PEP 302**, με
μεταγενέστερη επέκταση στο **PEP 420**.

Το **PEP 420** εισήγαγε τα *namespace πακέτα* στην Python 3.3. Το
**PEP 420** εισήγαγε επίσης το πρωτόκολλο "find_loader()" ως
εναλλακτική της "find_module()".

Το **PEP 366** περιγράφει την προσθήκη του χαρακτηριστικού
"__package__" για ρητές σχετικές εισαγωγές σε κύρια modules.

Το **PEP 328** εισήγαγε απόλυτες και ρητές σχετικές εισαγωγές και
αρχικά πρότεινε το "__name__" για τη σημασιολογία που το **PEP 366**
τελικά όρισε για το "__package__".

Το **PEP 338** ορίζει την εκτέλεση modules ως scripts.

Το **PEP 451** προσθέτει την ενθυλάκωση της κατάστασης εισαγωγής ανά
module σε spec αντικείμενα. Επίσης μεταφέρει το μεγαλύτερο μέρος των
τυποποιημένων ευθυνών των loaders πίσω στον μηχανισμό εισαγωγής. Αυτές
οι αλλαγές επιτρέπουν την απόσυρση αρκετών APIs στο σύστημα εισαγωγής
και επίσης την προσθήκη νέων μεθόδων σε finders και loaders.

-[ Υποσημειώσεις ]-

[1] Δείτε την "types.ModuleType".

[2] Η υλοποίηση του importlib αποφεύγει να χρησιμοποιεί απευθείας την
    τιμή επιστροφής. Αντ’ αυτού, λαμβάνει το αντικείμενο module
    αναζητώντας το όνομα του module στο "sys.modules". Έμμεση συνέπεια
    αυτού είναι ότι ένα εισηγμένο module μπορεί να αντικαταστήσει τον
    εαυτό του στο "sys.modules". Αυτή είναι μία ειδική συμπεριφορά της
    συγκεκριμένης υλοποίησης και δεν είναι εγγυημένο ότι λειτουργεί σε
    άλλες υλοποιήσεις της Python.
