1User guide 2========== 3 4.. currentmodule:: pyudev 5 6This guide gives an introduction in how to use pyudev for common operations 7like device enumeration or monitoring: 8 9.. contents:: 10 11A detailed reference is provided in the :doc:`API documentation <api/index>`. 12 13 14Getting started 15--------------- 16 17Import pyudev and verify that you're using the latest version: 18 19>>> import pyudev 20>>> pyudev.__version__ 21u'0.16' 22>>> pyudev.udev_version() 23181 24 25This prints the version of pyudev itself and of the underlying libudev_. 26 27 28A note on versioning 29-------------------- 30 31pyudev supports libudev_ 151 or newer, but still tries to cover the most recent 32libudev_ API completely. If you are using older libudev_ releases, some 33functionality of pyudev may be unavailable, simply because libudev_ is too old 34to support a specific feature. Whenever this is the case, the minimum required 35version of udev is noted in the documentation (see 36:attr:`Device.is_initialized` for an example). If no version is specified for 37an attribute or a method, it is available on all supported libudev_ versions. 38You can check the version of the underlying libudev_ with 39:func:`pyudev.udev_version()`. 40 41 42Enumerating devices 43------------------- 44 45A common use case is to enumerate available devices, or a subset thereof. But 46before you can do anything with pyudev, you need to establish a "connection" to 47the udev device database first. This connection is represented by a library 48:class:`Context`: 49 50>>> context = pyudev.Context() 51 52The :class:`Context` is the central object of pyudev and libudev_. You will 53need a :class:`Context` object for almost anything in pyudev. With the 54``context`` you can now enumerate the available devices: 55 56>>> for device in context.list_devices(): # doctest: +ELLIPSIS 57... device 58... 59Device(u'/sys/devices/LNXSYSTM:00') 60Device(u'/sys/devices/LNXSYSTM:00/LNXCPU:00') 61Device(u'/sys/devices/LNXSYSTM:00/LNXCPU:01') 62... 63 64By default, :meth:`list_devices()` yields all devices available on the system 65as :class:`Device` objects, but you can filter the list of devices with keyword 66arguments to enumerate all available partitions for example: 67 68>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'): 69... print(device) 70... 71Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda/sda1') 72Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda/sda2') 73Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda/sda3') 74 75The choice of the right filters depends on the use case and generally requires 76some knowledge about how udev classifies and categorizes devices. This is out 77of the scope of this guide. Poke around in ``/sys/`` to get a feeling for the 78udev-way of device handling, read the udev documentation or one of the 79tutorials in the net. 80 81The keyword arguments of :meth:`list_devices()` provide the most common filter 82operations. You can apply other, less common filters by calling one of the 83``match_*`` methods on the :class:`Enumerator` returned by of 84:meth:`list_devices()`. 85 86 87Accessing individual devices directly 88------------------------------------- 89 90If you just need a single specific :class:`Device`, you don't need to enumerate 91all devices with a specific filter criterion. Instead, you can directly create 92:class:`Device` objects from a device path (:meth:`Devices.from_path()`), by 93from a subsystem and device name (:meth:`Devices.from_name()`) or from a device 94file (:meth:`Devices.from_device_file()`). The following code gets the 95:class:`Device` object for the first hard disc in three different ways: 96 97>>> pyudev.Devices.from_path(context, '/sys/block/sda') 98Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda') 99>>> pyudev.Devices.from_name(context, 'block', 'sda') 100Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda') 101>>> pyudev.Devices.from_device_file(context, '/dev/sda') 102Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda') 103 104As you can see, you need to pass a :class:`Context` to both methods as 105reference to the udev database from which to retrieve information about the 106device. 107 108.. note:: 109 110 The :class:`Device` objects created in the above example refer to the same 111 device. Consequently, they are considered equal: 112 113 >>> pyudev.Devices.from_path(context, '/sys/block/sda') == pyudev.Devices.from_name(context, 'block', 'sda') 114 True 115 116 Whereas :class:`Device` objects referring to different devices are unequal: 117 118 >>> pyudev.Devices.from_name(context, 'block', 'sda') == pyudev.Devices.from_name(context, 'block', 'sda1') 119 False 120 121 122Querying device information 123--------------------------- 124 125As you've seen, :class:`Device` represents a device in the udev database. Each 126such device has a set of "device properties" (not to be confused with Python 127properties as created by :func:`property()`!) that describe the capabilities 128and features of this device as well as its relationship to other devices. 129 130Common device properties are also available as properties of a :class:`Device` 131object. For instance, you can directly query the :attr:`device_node` and the 132:attr:`device_type` of block devices: 133 134>>> for device in context.list_devices(subsystem='block'): 135... print('{0} ({1})'.format(device.device_node, device.device_type)) 136... 137/dev/sr0 (disk) 138/dev/sda (disk) 139/dev/sda1 (partition) 140/dev/sda2 (partition) 141/dev/sda3 (partition) 142 143For other udev properties, :class:`Device` provides a mapping interface 144to access the device properties by means of its properties attribute. 145 146>>> for device in context.list_devices(subsystem='block'): 147... print('{0} ({1})'.format(device.properties['DEVNAME'], device.properties['DEVTYPE'])) 148... 149/dev/sr0 (disk) 150/dev/sda (disk) 151/dev/sda1 (partition) 152/dev/sda2 (partition) 153/dev/sda3 (partition) 154 155.. warning:: 156 157 When filtering devices, you have to use the device property names. The 158 names of corresponding properties of :class:`Device` will generally **not** 159 work. Compare the following two statements: 160 161 >>> [device.device_node for device in context.list_devices(subsystem='block', DEVTYPE='partition')] 162 [u'/dev/sda1', u'/dev/sda2', u'/dev/sda3'] 163 >>> [device.device_node for device in context.list_devices(subsystem='block', device_type='partition')] 164 [] 165 166 167But you can also query many device properties that are not available as Python 168properties on the :class:`Device` object with a convenient mapping interface, 169like the filesystem type. :class:`Device` provides a convenient mapping 170interface for this purpose: 171 172>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'): 173... print('{0} ({1})'.format(device.device_node, device.properties.get('ID_FS_TYPE'))) 174... 175/dev/sda1 (ext3) 176/dev/sda2 (swap) 177/dev/sda3 (ext4) 178 179.. note:: 180 181 Such device specific properties may not be available on devices. Either use 182 ``get()`` to specify default values for missing properties, or be prepared 183 to catch :exc:`~exceptions.KeyError`. 184 185Most device properties are computed by udev rules from the driver- and 186device-specific "device attributes". The :attr:`Device.attributes` mapping 187gives you access to these attributes, but generally you should not need these. 188Use the device properties whenever possible. 189 190 191Examing the device hierarchy 192---------------------------- 193 194A :class:`Device` is part of a device hierarchy, and can have a 195:attr:`~Device.parent` device that more or less resembles the physical 196relationship between devices. For instance, the :attr:`~Device.parent` of 197partition devices is a :class:`Device` object that represents the disc the 198partition is located on: 199 200>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'): 201... print('{0} is located on {1}'.format(device.device_node, device.parent.device_node)) 202... 203/dev/sda1 is located on /dev/sda 204/dev/sda2 is located on /dev/sda 205/dev/sda3 is located on /dev/sda 206 207Generally, you should not rely on the direct parent-child relationship between 208two devices. Instead of accessing the parent directly, search for a parent 209within a specific subsystem, e.g. for the parent ``block`` device, with 210:meth:`~Device.find_parent()`: 211 212>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'): 213... print('{0} is located on {1}'.format(device.device_node, device.find_parent('block').device_node)) 214... 215/dev/sda1 is located on /dev/sda 216/dev/sda2 is located on /dev/sda 217/dev/sda3 is located on /dev/sda 218 219This also save you the tedious work of traversing the device tree manually, if 220you are interested in grand parents, like the name of the PCI slot of the SCSI 221or IDE controller of the disc that contains a partition: 222 223>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'): 224... print('{0} attached to PCI slot {1}'.format(device.device_node, device.find_parent('pci')['PCI_SLOT_NAME'])) 225... 226/dev/sda1 attached to PCI slot 0000:00:0d.0 227/dev/sda2 attached to PCI slot 0000:00:0d.0 228/dev/sda3 attached to PCI slot 0000:00:0d.0 229 230 231Monitoring devices 232------------------ 233 234Synchronous monitoring 235~~~~~~~~~~~~~~~~~~~~~~ 236 237The Linux kernel emits events whenever devices are added, removed (e.g. a USB 238stick was plugged or unplugged) or have their attributes changed (e.g. the 239charge level of the battery changed). With :class:`pyudev.Monitor` you can 240react on such events, for example to react on added or removed mountable 241filesystems: 242 243>>> monitor = pyudev.Monitor.from_netlink(context) 244>>> monitor.filter_by('block') 245>>> for device in iter(monitor.poll, None): 246... if 'ID_FS_TYPE' in device: 247... print('{0} partition {1}'.format(device.action, device.get('ID_FS_LABEL'))) 248... 249add partition MULTIBOOT 250remove partition MULTIBOOT 251 252After construction of a monitor, you can install an event filter on the monitor 253using :meth:`~Monitor.filter_by()`. In the above example only events from the 254``block`` subsystem are handled. 255 256.. note:: 257 258 Always prefer :meth:`~Monitor.filter_by()` and 259 :meth:`~Monitor.filter_by_tag()` over manually filtering devices (e.g. by 260 ``device.subsystem == 'block'`` or ``tag in device.tags``). These methods 261 install the filter on the *kernel side*. A process waiting for events is 262 thus only woken up for events that match these filters. This is much nicer 263 in terms of power consumption and system load than executing filters in the 264 process itself. 265 266Eventually, you can receive events from the monitor. As you can see, a 267:class:`Monitor` is iterable and synchronously yields occurred events. If you 268iterate over a :class:`Monitor`, you will synchronously receive events in an 269endless loop, until you raise an exception, or ``break`` the loop. 270 271This is the quick and dirty way of monitoring, suitable for small scripts or 272quick experiments. In most cases however, simply iterating over the monitor is 273not sufficient, because it blocks the main thread, and can only be stopped if 274an event occurs (otherwise the loop is not entered and you have no chance to 275``break`` it). 276 277 278Asynchronous monitoring 279~~~~~~~~~~~~~~~~~~~~~~~ 280 281For such use cases, pyudev provides asynchronous monitoring with 282:class:`MonitorObserver`. You can use it to log added and removed mountable 283filesystems to a file, for example: 284 285>>> monitor = pyudev.Monitor.from_netlink(context) 286>>> monitor.filter_by('block') 287>>> def log_event(device): 288... if 'ID_FS_TYPE' in device.properties: 289... with open('filesystems.log', 'a+') as stream: 290... print('{0} - {1}'.format(device.action, device.get('ID_FS_LABEL')), file=stream) 291... 292>>> observer = pyudev.MonitorObserver(monitor, callback=log_event) 293>>> observer.start() 294 295The ``observer`` gets a callback (``log_event()`` in this case) which is 296asynchronously invoked on every event emitted by the underlying ``monitor`` 297after the observer has been started using :meth:`~threading.Thread.start()`. 298 299.. warning:: 300 301 The callback is invoked from a *different* thread than the one in which the 302 ``observer`` was created. Be sure to protect access to shared resource 303 properly when you access them from the callback (e.g. by locking). 304 305The ``observer`` can be stopped at any moment using :meth:`~MonitorObserver.stop()``: 306 307>>> observer.stop() 308 309.. warning:: 310 311 Do *not* call :meth:`~MonitorObserver.stop()` from the event handler, 312 neither directly nor indirectly. Use :meth:`~MonitorObserver.send_stop()` 313 if you need to stop monitoring from inside the event handler. 314 315 316GUI toolkit integration 317~~~~~~~~~~~~~~~~~~~~~~~ 318 319If you're using a GUI toolkit, you already have the event system of the GUI 320toolkit at hand. pyudev provides observer classes that seamlessly integration 321in the event system of the GUI toolkit and relieve you from caring with 322synchronisation issues that would occur with thread-based monitoring as 323implemented by :class:`MonitorObserver`. 324 325pyudev supports all major GUI toolkits available for Python: 326 327- Qt_ 5 using :mod:`pyudev.pyqt5` 328- Qt_ 4 using :mod:`pyudev.pyqt4` for the PyQt4_ binding or :mod:`pyudev.pyside` 329 for the PySide_ binding 330- PyGtk_ 2 using :mod:`pyudev.glib` 331- wxWidgets_ and wxPython_ using :mod:`pyudev.wx` 332 333Each of these modules provides an observer class that observers the monitor 334asynchronously and emits proper signals upon device events. 335 336For instance, the above example would look like this in a PySide_ application: 337 338>>> from pyudev.pyside import QUDevMonitorObserver 339>>> monitor = pyudev.Monitor.from_netlink(context) 340>>> observer = QUDevMonitorObserver(monitor) 341>>> observer.deviceEvent.connect(log_event) 342>>> monitor.start() 343 344Device objects as booleans 345~~~~~~~~~~~~~~~~~~~~~~~~~~ 346The use of a Device object in a boolean context as a shorthand for a comparison 347with None is an error. 348 349The Device class inherits from the abstract Mapping class, as it maps udev 350property names to their values. Consequently, if a Device object has no udev 351properties, an unusual but not impossible occurance, the object is 352interpreted as False in a boolean context. 353 354.. _pypi: https://pypi.python.org/pypi/pyudev 355.. _libudev: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ 356.. _Qt: http://qt.io/developers/ 357.. _PyQt5: https://riverbankcomputing.co.uk/software/pyqt/intro 358.. _PyQt4: https://riverbankcomputing.co.uk/software/pyqt/intro 359.. _PySide: http://wiki.qt.io/PySide 360.. _PyGtk: http://www.pygtk.org/ 361.. _wxWidgets: http://wxwidgets.org 362.. _wxPython: http://www.wxpython.org 363