USB-Stick sicher entfernen in python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo zusammen,

Google hat mich leider nicht weitergebracht.
Ich habe nur herausbekommen, dass ich statt dem os.system den subprocess Aufruf nutzen soll um in Python einen gemounteten USB-Stick sicher zu entfernen.
Weiß jemand wie genau?

LG Tina
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Man sollte grundsaetzlich subprocess benutzen, aber mit dem sicheren auswerfen hat das erstmal nichts zu tun.

Du musst ja eigentlich rausfinden, wo das System deinen USB-Stick hingemountet hat. Ich benutze dazu das pyudev-Modul, mit dem man darueber informiert wird, wenn diverse Geraete angemeldet und abgemeldet werden.

Folgendes ist Code, den ich dazu geschrieben habe, der aber auch noch eine handvoll anderer Sachen macht. Damit kannst du versuchen zu starten. Das ganze BCM-Zeug ist fuer dich zB uninteressant und kann weg.

Code: Alles auswählen

class LinuxDeviceMonitor():

    FILTER = "block"

    def __init__(self):
        import pyudev
        self._context = pyudev.Context()
        self._monitor = pyudev.Monitor.from_netlink(self._context)
        self._monitor.start()
        self._devices = set()
        self._listeners = []
        self._bcm2710_listeners = []
        self.rescan()


    def rescan(self):
        self._process_devices(
            self._context.list_devices(),
            action="add"
        )


    def add_listener(self, listener):
        self._listeners.append(listener)


    def remove_listener(self, listener):
        self._listeners.remove(listener)


    def add_bcm2710boot_device_listener(self, listener):
        self._bcm2710_listeners.append(listener)


    def remove_bcm2710boot_device_listener(self, listener):
        self._listeners.remove(listener)


    def fileno(self):
        return self._monitor.fileno()


    @property
    def devices(self):
        import pyudev
        return [pyudev.Device.from_path(self._context, device).get("DEVNAME") for device in self._devices]


    def process_incoming(self, *_args):
        from pyudev._util import eintr_retry_call
        read_device = partial(eintr_retry_call, self._monitor.poll, timeout=0)
        self._process_devices(iter(read_device, None))


    def _process_devices(self, devices, action=None):
        old = set(self._devices)
        for device in devices:
            action = device.action if action is None else action
            logger.debug("action: %s - device: %r", action, device)
            if action in ("add", "change") and self._is_usb_mass_storage_device(device):
                self._devices.add(device.sys_path)
            elif "remove" == action and device.sys_path in self._devices:
                self._devices.remove(device.sys_path)
            elif self._is_bcm2710_boot_device(device):
                self._emit_bcm2710_boot_device_signal(device, action)
        if self._devices != old:
            devices = self.devices
            for listener in self._listeners:
                listener(devices)


    def _is_bcm2710_boot_device(self, device):
        return "BCM2710_Boot" == device.get("ID_MODEL", "")


    def _emit_bcm2710_boot_device_signal(self, device, action):
        logger.info("BCM2710 boot device found: %r, %s", device, action)
        for listener in self._bcm2710_listeners:
            listener(device, action)


    def _is_usb_mass_storage_device(self, device):
        def dev_path(device, name):
            return "{}/{}".format(device.sys_path, name)

        def removable(device):
            removable_path = dev_path(device, "removable")
            try:
                return os.path.exists(removable_path) and int(open(removable_path).read())
            except ValueError:
                return False

        def has_size(device):
            size_path = dev_path(device, "size")
            try:
                return os.path.exists(size_path) and int(open(size_path).read())
            except ValueError:
                return False

        return device.subsystem == "block" and removable(device) and has_size(device)
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nachtrag, bevor das geschimpfe losgeht: der lokale import ist hier genau richtig, da das gesamte Modul die entstehenden Mountpoints plattformunabhaengig verwaltet. Auf dem Mac ist pyudev zu importieren natuerlich sinnlos. Darum passiert das hier im Konstruktor und diversen Methoden, nachdem an anderer Stelle eine Weiche entschieden hat, welche Klasse instantiiert wird.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Und noch ein Nachtrag: die Klasse wird bei mir in einer tkinter-GUI eingesetzt, wo ich den Filedeskriptor (fileno()) registriere, um ueber neue Daten benachrichtig zu werden:

Code: Alles auswählen

        self._root.tk.createfilehandler(
            device_monitor.fileno(),
            tk.READABLE,
            device_monitor.process_incoming
        )
So aehnlich kann man in Qt natuerlich auch vorgehen (du benutzt doch qt, wenn ich mich recht erinnere?), da waere das http://doc.qt.io/qt-5/qsocketnotifier.html
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Hallo deets,
ja ich nutze QT. Hört sich trotzdem sehr kompliziert für mich an :o
Der USB-Stick wird immer auf das gleiche Verzeichnis gemountet /media/usb-stick
Deshalb muss ich nicht herausfinden wohin er ihn eingehängt hat 😊
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na dann musst du doch nur sudo umount Pfad machen.
Tinker232
User
Beiträge: 50
Registriert: Mittwoch 25. Juli 2018, 13:45

Es funktioniert nun! Das File war nicht geschlossen ;)
Danke
Antworten