pty — Βοηθητικά προγράμματα ψευδοτερματικού

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


Το pty module ορίζει λειτουργίες για τη διαχείριση της έννοιας του ψευδοτερματικού: την εκκίνηση μιας άλλης διαδικασίας και την ικανότητα να γράφει και να διαβάζει από το τερματικό πρόγραμμα ελέγχου της προγραμματιστικά.

Η διαχείριση ψευδοτερματικού είναι εξαιρετικά εξαρτώμενη από την πλατφόρμα. Αυτός ο κώδικας έχει δοκιμαστεί κυρίως σε Linux, FreeBSD και macOS (υποτίθεται ότι λειτουργεί σε άλλες πλατφόρμες POSIX αλλά δεν έχει δοκιμαστεί διεξοδώς).

Το pty module ορίζει τις ακόλουθες λειτουργίες:

pty.fork()

Αντιγραφή. Συνδέστε το τερματικό ελέγχου του παιδιού σε ένα ψευδοτερματικό. Η επιστρεφόμενη τιμή είναι (pid, fd). Σημειώστε ότι το παιδί λαμβάνει pid 0, και το fd είναι invalid. Η επιστρεφόμενη τιμή του γονέα είναι το pid του παιδιού, και το fd είναι ένας περιγραφέας αρχείου συνδεδεμένος με το τερματικό ελέγχου του παιδιού (και επίσης με την τυπική είσοδο και έξοδο του παιδιού).

Προειδοποίηση

Σε macOS η χρήση αυτής της λειτουργίας είναι μη ασφαλής όταν αναμιγνύεται με τη χρήση ανώτερων συστημικών APIs, και αυτό περιλαμβάνει τη χρήση urllib.request.

pty.openpty()

Ανοίξτε ένα νέο ζεύγος ψευδοτερματικών, χρησιμοποιώντας os.openpty() αν είναι δυνατόν, ή κώδικα προσομοίωσης για γενικά συστήματα Unix. Επιστρέφει ένα ζεύγος περιγραφέων αρχείων (master, slave), για το κύριο και το δευτερεύον άκρο, αντίστοιχα.

pty.spawn(argv[, master_read[, stdin_read]])

Δημιουργήστε μια διαδικασία, και συνδέστε το τερματικό ελέγχου της με την τυπική είσοδο/έξοδο της τρέχουσας διαδικασίας. Αυτό χρησιμοποιείται συχνά για να αποπροσανατολίσει προγράμματα που επιμένουν να διαβάζουν από το τερματικό ελέγχου. Αναμένεται ότι η διαδικασία που ξεκίνησε πίσω από το pty θα τερματιστεί τελικά, και όταν το κάνει spawn θα επιστρέψει.

Ένας βρόχος αντιγράφει την STDIN της τρέχουσας διαδικασίας στο παιδί και τα δεδομένα που λαμβάνονται από το παιδί στην STDOUT της τρέχουσας διαδικασίας. Δεν ειδοποιείται το παιδί εάν η STDIN της τρέχουσας διαδικασίας κλείσει.

Οι συναρτήσεις master_read και stdin_read λαμβάνουν έναν περιγραφέα αρχείου από τον οποίο πρέπει να διαβάσουν, και θα πρέπει πάντα να επιστρέφουν ένα byte string. Για να αναγκάσετε το spawn να επιστρέψει πριν τερματιστεί η διαδικασία παιδί, θα πρέπει να επιστραφεί ένας κενός πίνακας byte για να σηματοδοτήσει το τέλος του αρχείου.

Η προεπιλεγμένη υλοποίηση και για τις δύο συναρτήσεις θα διαβάσει και θα επιστρέψει έως και 1024 bytes κάθε φορά που καλείται η συνάρτηση. Στην κλήση master_read περνάει ο περιγραφέας αρχείου του κύριου ψευδοτερματικού για να διαβάσει την έξοδο από τη διαδικασία παιδί, και στην stdin_read περνάει ο περιγραφέας αρχείου 0, για να διαβάσει από την τυπική είσοδο της διαδικασίας γονέα.

Επιστροφή ενός κενό πίνακα byte από οποιαδήποτε κλήση ερμηνεύεται ως κατάσταση τέλους-αρχείου (EOF), και αυτή η κλήση δεν θα κληθεί μετά από αυτό. Εάν η stdin_read σηματοδοτήσει EOF, το τερματικό ελέγχου δεν μπορεί πλέον να επικοινωνήσει με τη διαδικασία γονέα ή τη διαδικασία παιδί. Εκτός εάν η διαδικασία παιδί θα τερματιστεί χωρίς καμία είσοδο, το spawn θα συνεχίσει να εκτελείται επ” άπειρον. Εάν η master_read σηματοδοτήσει EOF, προκύπτει η ίδια συμπεριφορά (τουλάχιστον στο linux).

Επιστρέφει την τιμή κατάστασης εξόδου από os.waitpid() στη διαδικασία παιδί.

Η συνάρτηση os.waitstatus_to_exitcode() μπορεί να χρησιμοποιηθεί για να μετατρέψει την κατάσταση εξόδου σε κωδικό εξόδου.

Κάνει raise ένα auditing event pty.spawn με όρισμα argv.

Άλλαξε στην έκδοση 3.4: Η συνάρτηση spawn() τώρα επιστρέφει την τιμή κατάστασης από os.waitpid() στη διαδικασία παιδί.

Παράδειγμα

Το ακόλουθο πρόγραμμα λειτουργεί όπως η εντολή Unix script(1), χρησιμοποιώντας ένα ψευδοτερματικό για να καταγράψει όλη την είσοδο και έξοδο μιας συνεδρίας τερματικού σε ένα «typescript».

import argparse
import os
import pty
import sys
import time

parser = argparse.ArgumentParser()
parser.add_argument('-a', dest='append', action='store_true')
parser.add_argument('-p', dest='use_python', action='store_true')
parser.add_argument('filename', nargs='?', default='typescript')
options = parser.parse_args()

shell = sys.executable if options.use_python else os.environ.get('SHELL', 'sh')
filename = options.filename
mode = 'ab' if options.append else 'wb'

with open(filename, mode) as script:
    def read(fd):
        data = os.read(fd, 1024)
        script.write(data)
        return data

    print('Script started, file is', filename)
    script.write(('Script started on %s\n' % time.asctime()).encode())

    pty.spawn(shell, read)

    script.write(('Script done on %s\n' % time.asctime()).encode())
    print('Script done, file is', filename)