File pyudev-0.24.1.obscpio of Package python-pyudev

07070100000000000081A40000000000000000000000016443203100000211000000000000000000000000000000000000001900000000pyudev-0.24.1/.isort.cfg[settings]
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
use_parentheses=True
line_length=88

sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCAL,LOCALFOLDER
default_section=THIRDPARTY

import_heading_future=isort: FUTURE
import_heading_stdlib=isort: STDLIB
import_heading_thirdparty=isort: THIRDPARTY
import_heading_firstparty=isort: FIRSTPARTY
import_heading_local=isort: LOCAL

# All items above should be the same for every
# Stratis project. The items below vary with
# each project.
known_local=pyudev
07070100000001000081A4000000000000000000000001644320310000367D000000000000000000000000000000000000001A00000000pyudev-0.24.1/CHANGES.rst0.24.1
======
Recommended development release: Fedora 37

- Add support for PySide6:
  https://github.com/pyudev/pyudev/pull/487

- Add missing 'priority' argument for GLib.to_add_watch()
  https://github.com/pyudev/pyudev/pull/479

- Tidies and Maintenance:
  https://github.com/pyudev/pyudev/pull/486
  https://github.com/pyudev/pyudev/pull/485
  https://github.com/pyudev/pyudev/pull/484
  https://github.com/pyudev/pyudev/pull/481
  https://github.com/pyudev/pyudev/pull/480
  https://github.com/pyudev/pyudev/pull/477
  https://github.com/pyudev/pyudev/pull/474


0.24.0
======
Recommended development release: Fedora 36

- Remove a bunch of Python 2 code:
  https://github.com/pyudev/pyudev/pull/468
  https://github.com/pyudev/pyudev/pull/460
  https://github.com/pyudev/pyudev/pull/455
  https://github.com/pyudev/pyudev/pull/358

- Tidies and Maintenance:
  https://github.com/pyudev/pyudev/pull/471
  https://github.com/pyudev/pyudev/pull/466
  https://github.com/pyudev/pyudev/pull/465
  https://github.com/pyudev/pyudev/pull/464
  https://github.com/pyudev/pyudev/pull/462
  https://github.com/pyudev/pyudev/pull/461
  https://github.com/pyudev/pyudev/pull/459
  https://github.com/pyudev/pyudev/pull/458
  https://github.com/pyudev/pyudev/pull/456
  https://github.com/pyudev/pyudev/pull/454
  https://github.com/pyudev/pyudev/pull/447
  https://github.com/pyudev/pyudev/pull/445


0.23.2
======
Recommended development release: Fedora 35

- New release: 0.23.2:
  https://github.com/pyudev/pyudev/pull/444

- Tidies and Maintenance:
  https://github.com/pyudev/pyudev/pull/445
  https://github.com/pyudev/pyudev/pull/443
  https://github.com/pyudev/pyudev/pull/442


0.23.1
======
Recommended development release: Fedora 34

- Update to version 0.23.1:
  https://github.com/pyudev/pyudev/pull/440

- Check for existence of _hasattr attribute in Context.__del__ method:
  https://github.com/pyudev/pyudev/issues/421
  https://github.com/pyudev/pyudev/pull/439

- Tidies and Maintenance:
  https://github.com/pyudev/pyudev/pull/438

0.23.0
======
Recommended development release: Fedora 34

- Update to version 0.23.0:
  https://github.com/pyudev/pyudev/pull/427

- Officially drop support for Python 2:
  https://github.com/pyudev/pyudev/pull/414

- Remove external mock dependency:
  https://github.com/pyudev/pyudev/pull/409

- Enable GLib unit tests:
  https://github.com/pyudev/pyudev/pull/400

- Switch to new style gi.repository.GLib imports:
  https://github.com/pyudev/pyudev/pull/399

- Tidies and Maintenance:
  https://github.com/pyudev/pyudev/pull/432
  https://github.com/pyudev/pyudev/pull/430
  https://github.com/pyudev/pyudev/pull/429
  https://github.com/pyudev/pyudev/pull/428
  https://github.com/pyudev/pyudev/pull/424
  https://github.com/pyudev/pyudev/pull/423
  https://github.com/pyudev/pyudev/pull/418
  https://github.com/pyudev/pyudev/pull/415
  https://github.com/pyudev/pyudev/pull/413


0.22.0 (Jan, 2020)
==================

- Add a six-enabled move for collections move imports:
  https://github.com/pyudev/pyudev/pull/386
- Fix any newly introduced pylint errors
- Numerous improvements or updates to the test infrastructure
- A number of test updates
- Require yapf 0.21.0 for Python formatting
- Various documentation fixes and updates


0.21.0 (July 20, 2016)
======================

- Deprecate use of Device object as mapping from udev property names to values.
- Add a Properties class and a Device.properties() method for udev properties.
- Fix places where Device object was incorrectly used in a boolean context.
- Return an empty string, not None, if the property value is an empty string.
- Exceptions re-raised from libudev calls now have a message string.
- Insert a warning about using a Device in a boolean context in docs.
- Infrastructure for vagrant tests is removed.
- Various internal refactorings.
- Extensive test improvements.
- Numerous documentation fixes.

0.20.0 (April 29, 2016)
=======================

- Remove parsing code added in previous release.
- No longer do CI for Python 2.6.
- Eliminate all wildcard imports and __all__ statements.
- No longer use deprecated Device.from_sys_path() method.
- Minor pylint induced changes.
- Documentation fixes.

0.19.0 (Feb 3, 2016)
==================

- Restore raising KeyError by Attributes.as* methods when attribute not found.
- Explicitly require six module.
- Never raise a DeviceNotFoundError when iterating over a device enumeration.
- Device.subsystem() now returns None if device has no subsystem.
- Add DeprecationWarnings to deprecated Device methods.
- Replace "/" with "!" in Device.from_name() sys_name parameter.
- Add some unstable classes for parsing some kinds of values.
- Make version info more like Python's including micro numbers and levels.
- Refactor some internal modules into subdirectories.
- Work on tests and reproducers.

0.18 (Dec 1, 2015)
===================

- DeviceNotFoundError is no longer a subtype of LookupError
- Added support for pyqt5 monitor observer
- Added discover module, which looks up a device on limited information
- Attributes class no longer extends Mapping, extends object instead
- Attributes class no longer inherits [] operator, Mapping methods
- Attributes class objects are no longer iterable
- Attributes.available_attributes property added
- Attributes.get() method, with usual semantics, defined
- Device.from_* methods are deprecated, uses Devices.from_* methods instead
- Device.from_device_file() now raises DeviceNotFoundByFileError
- Device.from_device_number() now raises DeviceNotFoundByNumberError
- Devices.from_interface_index() method added
- Devices.from_kernel_device() method added
- Numerous testing infrastructure changes

0.17 (Aug 26, 2015)
=====================

- #52: Remove global libudev object
- #57: Really start the monitor on :meth:`pyudev.Monitor.poll()`
- #60: Do not use :meth:`select.select` to avoid hitting its file descriptor
  limit
- #58: Force non-blocking IO in :class:`pyudev.Monitor` to avoid blocking on
  receiving the device
- #63: Set proper flags on pipe fds.
- #65: Handle irregular polling events properly.
- #50: Add :class:`pyudev.wx.MonitorObserver` and deprecate
  :class:`pyudev.wx.WxUDevMonitorObserver`
- #50: Add :class:`pyudev.glib.MonitorObserver` and deprecate
  :class:`pyudev.glib.GUDevMonitorObserver`
- #50: Add :class:`pyudev.pyqt4.MonitorObserver` and deprecate
  :class:`pyudev.pyqt4.QUDevMonitorObserver`
- #50: Add :class:`pyudev.pyside.MonitorObserver` and deprecate
  :class:`pyudev.pyside.QUDevMonitorObserver`
- Add a wrapper function to retry interruptible system calls.


0.16.1 (Aug 02, 2012)
=====================

- #53: Fix source distribution
- #54: Fix typo in test


0.16 (Jul 25, 2012)
===================

- Remove :meth:`pyudev.Monitor.from_socket`.
- Deprecate :meth:`pyudev.Device.traverse()` in favor of
  :attr:`pyudev.Device.ancestors`.
- #47: Deprecate :meth:`pyudev.Monitor.receive_device` in favor of
  :attr:`pyudev.Monitor.poll`.
- #47: Deprecate :attr:`pyudev.Monitor.enable_receiving` in favor of
  :attr:`pyudev.Monitor.start`.
- #47: Deprecate :attr:`pyudev.Monitor.__iter__` in favor of explicit looping or
  :class:`pyudev.MonitorObserver`.
- #49: Deprecate ``event_handler`` to :class:`pyudev.MonitorObserver` in favour
  of ``callback`` argument.
- #46: Continuously test pyudev on Travis-CI.
- Add :attr:`pyudev.Device.ancestors`.
- Add :attr:`pyudev.Device.action`.
- #10: Add :attr:`pyudev.Device.sequence_number`.
- #47: Add :meth:`pyudev.Monitor.poll`.
- #47: Add :attr:`pyudev.Monitor.started`.
- #49: Add ``callback`` argument to :class:`pyudev.Monitor`.
- :meth:`pyudev.Monitor.start` can be called repeatedly.
- #45: Get rid of 2to3
- #43: Fix test failures on Python 2.6
- Fix signature in declaration of ``udev_monitor_set_receive_buffer_size``.
- #44: Test wrapped signatures with help of ``gccxml``.
- Fix compatibility with udev 183 and newer in :class:`pyudev.Context`.
- :meth:`pyudev.MonitorObserver.stop` can be called from the observer thread.


0.15 (Mar 1, 2012)
==================

- #20: Add :meth:`~pyudev.Monitor.remove_filter()`.
- #40: Add user guide to the documentation.
- #39: Add :meth:`pyudev.Device.from_device_file()`.
- :data:`errno.EINVAL` from underlying libudev functions causes
  :exc:`~exceptions.ValueError` instead of :exc:`~exceptions.EnvironmentError`.
- :class:`pyudev.MonitorObserver` calls
  :meth:`pyudev.Monitor.enable_receiving()` when started.
- #20: :meth:`pyudev.Monitor.filter_by()` and
  :meth:`pyudev.Monitor.filter_by_tag()` can be called after
  :meth:`pyudev.Monitor.enable_receiving()`.


0.14 (Feb 10, 2012)
===================

- Host documentation at http://pyudev.readthedocs.org (thanks to the
  readthedocs.org team for this service)
- #37: Add :class:`pyudev.wx.WxUDevMonitorObserver` for wxPython (thanks to
  Tobias Eberle).
- Add :class:`pyudev.MonitorObserver`.
- Add :attr:`pyudev.glib.GUDevMonitorObserver.enabled`,
  :attr:`pyudev.pyqt4.QUDevMonitorObserver.enabled` and
  :attr:`pyudev.pyside.QUDevMonitorObserver.enabled`.


0.13 (Nov 4, 2011)
==================

- #36: Add :meth:`pyudev.Monitor.set_receive_buffer_size` (thanks to Rémi
  Rérolle).
- Add :meth:`pyudev.Enumerator.match_parent`.
- Add ``parent`` keyword argument to :meth:`pyudev.Enumerator.match()`.
- #31: Add :meth:`pyudev.Enumerator.match_attribute`.
- Add ``nomatch`` argument to :meth:`pyudev.Enumerator.match_subsystem` and
  :meth:`pyudev.Enumerator.match_attribute`.
- Remove :meth:`pyudev.Enumerator.match_children` in favour of
  :meth:`pyudev.Enumerator.match_parent`.
- #34: :class:`pyudev.Device.tags` returns a :class:`pyudev.Tags` object.
- :attr:`pyudev.Device.children` requires udev version 172 now


0.12 (Aug 31, 2011)
===================

- #32: Fix memory leak.
- #33: Fix Python 3 support for :mod:`pyudev.glib`.
- Fix license header in :mod:`pyudev._compat`.


0.11 (Jun 26, 2011)
===================

- #30: Add :attr:`pyudev.Device.sys_number`.
- #29: Add :meth:`pyudev.Device.from_device_number`
- #29: Add :attr:`pyudev.Device.device_number`.
- Support PyPy.


0.10 (Apr 20, 2011)
===================

- Add :attr:`pyudev.__version_info__`
- Add :attr:`pyudev.Device.device_type`
- :class:`pyudev.Context`, :class:`pyudev.Enumerator`, :class:`pyudev.Device`
  and :class:`pyudev.Monitor` can directly be passed to
  :mod:`ctypes`-wrapped functions.
- #24: Add :attr:`pyudev.Context.run_path`.


0.9 (Mar 09, 2011)
==================

- #21: Add :meth:`pyudev.Device.find_parent`.
- #22: Add :meth:`pyudev.Monitor.filter_by_tag`.
- Add :attr:`pyudev.Context.log_priority`.
- Improve error reporting, if libudev is missing.


0.8 (Jan 08, 2011)
==================

- #16: Add :meth:`pyudev.Enumerator.match`.
- Add keyword arguments to :meth:`pyudev.Context.list_devices()`.
- #19: Add :meth:`pyudev.Enumerator.match_sys_name`.
- #18: Add :func:`pyudev.udev_version()`.
- #17: Add :attr:`pyudev.Device.is_initialized`.
- #17: Add :attr:`pyudev.Device.time_since_initialized`.
- #17: Add :meth:`pyudev.Enumerator.match_is_initialized`
- Fix support for earlier releases of udev.
- Document minimum udev version for all affected attributes.


0.7 (Nov 15, 2010)
==================

- #15: Add :mod:`pyudev.glib.GUDevMonitorObserver`.


0.6 (Oct 03, 2010)
==================

- #8: Add :attr:`pyudev.Device.tags`.
- #8: Add :meth:`pyudev.Enumerator.match_tag`.
- #11: Add :meth:`pyudev.Device.from_environment`
- #5: Add :mod:`pyudev.pyside`
- #14: Remove apipkg_ dependency.
- #14: Require explicit import of :mod:`pyudev.pyqt4`.
- Fix licence headers in source files.

.. _apipkg: http://pypi.python.org/pypi/apipkg/


0.5 (Sep 06, 2010)
==================

- Support Python 3.
- #6: Add :attr:`pyudev.Device.attributes` (thanks to Daniel Lazzari).
- #6: Add :class:`pyudev.Attributes` (thanks to Daniel Lazzari).
- #7: :attr:`pyudev.Device.context` and :attr:`pyudev.Monitor.context` are
  part of the public API.
- #9: Add :attr:`pyudev.Device.driver`.
- #12: Add :meth:`pyudev.Device.from_name`.
- Rename :exc:`pyudev.NoSuchDeviceError` to :exc:`pyudev.DeviceNotFoundError`.
- :meth:`pyudev.Device.from_sys_path` raises
  :exc:`pyudev.DeviceNotFoundAtPathError`.
- #13: Fix :exc:`~exceptions.AttributeError` in
  :attr:`pyudev.Device.device_node`.
- Improve and extend documentation.
- Add more tests.


0.4 (Aug 23, 2010)
==================

API changes
-----------

- #3: Rename :mod:`udev` to :mod:`pyudev`.
- #3: Rename :mod:`qudev` to :mod:`pyudev.pyqt4`.
- Add :meth:`pyudev.Device.from_path`.
- :meth:`pyudev.Device.from_sys_path` raises :exc:`pyudev.NoSuchDeviceError`.
- :meth:`pyudev.Monitor.receive_device` raises
  :exc:`~exceptions.EnvironmentError`.
- ``errno``, ``strerror`` and ``filename`` attributes of
  :class:`~exceptions.EnvironmentError` exceptions have meaningful content.
- Fix :exc:`~exceptions.NameError` in :meth:`pyudev.Monitor.from_socket`
- ``subsystem`` argument to :meth:`pyudev.Monitor.filter_by` is mandatory.
- Delete underlying C objects if :class:`pyudev.Device` is garbage-collected.
- Fix broken signal emitting in :class:`pyudev.pyqt4.QUDevMonitorObserver`.


0.3 (Jul 28, 2010)
==================

- #1: Fix documentation to reflect the actual behaviour of the underlying
  API
- Raise :exc:`~exceptions.TypeError` if :class:`udev.Device` are compared with
  ``>``, ``>=``, ``<`` or ``<=``.
- Add :meth:`udev.Enumerator.match_children`.
- Add :attr:`udev.Device.children`.
- Add :meth:`qudev.QUDevMonitorObserver.deviceChanged`.
- Add :meth:`qudev.QUDevMonitorObserver.deviceMoved`.


0.2 (Jun 28, 2010)
==================

- Add :class:`udev.Monitor`.
- Add :meth:`udev.Device.asbool`.
- Add :meth:`udev.Device.asint`.
- Remove type magic in :meth:`udev.Device.__getitem__`.
- Add :mod:`qudev`.


0.1 (May 03, 2010)
==================

- Initial release.
- Add :class:`udev.Context`.
- Add :class:`udev.Device`.
- Add :class:`udev.Enumerator`.
07070100000002000081A40000000000000000000000016443203100006740000000000000000000000000000000000000001600000000pyudev-0.24.1/COPYING		  GNU LESSER GENERAL PUBLIC LICENSE
		       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
     51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

		  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.
  
  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

			    NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

		     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the library's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

Also add information on how to contact you by electronic and paper mail.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  library `Frob' (a library for tweaking knobs) written by James Random Hacker.

  <signature of Ty Coon>, 1 April 1990
  Ty Coon, President of Vice

That's all there is to it!


07070100000003000081A400000000000000000000000164432031000000EE000000000000000000000000000000000000001A00000000pyudev-0.24.1/MANIFEST.inrecursive-include src/pyudev *.py
recursive-include doc *.rst *.py *.html
recursive-include tests *.py
recursive-exclude tests/.hypothesis *.py
include .isort.cfg
include CHANGES.rst COPYING README.rst
include setup.py
include pytest.ini
07070100000004000081A400000000000000000000000164432031000005C9000000000000000000000000000000000000001700000000pyudev-0.24.1/Makefile# Copyright (C) 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

package:
	(umask 0022; python -m build; python -m twine check --strict ./dist/*)

lint:
	pylint setup.py
	pylint src/pyudev/_os

PYREVERSE_OPTS = --output=pdf
view:
	-rm -Rf _pyreverse
	mkdir _pyreverse
	PYTHONPATH=src pyreverse ${PYREVERSE_OPTS} --project="pyudev" src/pyudev
	mv classes_pyudev.pdf _pyreverse
	mv packages_pyudev.pdf _pyreverse

archive:
	git archive --output=./archive.tar.gz HEAD

.PHONY: test-travis
test-travis:
	py.test --junitxml=tests.xml  -rfEsxX

.PHONY: fmt
fmt:
	isort setup.py src tests
	black .

.PHONY: fmt-travis
fmt-travis:
	isort --diff --check-only setup.py src tests
	black . --check

.PHONY: yamllint
yamllint:
	yamllint --strict .github/workflows/*.yml
07070100000005000081A40000000000000000000000016443203100000E7C000000000000000000000000000000000000001900000000pyudev-0.24.1/README.rst######
pyudev
######

.. image:: https://secure.travis-ci.org/pyudev/pyudev.png?branch=develop
   :target: http://travis-ci.org/pyudev/pyudev

http://pyudev.readthedocs.org

pyudev is a LGPL_ licensed, pure Python_ binding for libudev_, the device and
hardware management and information library for Linux.  It supports almost all
libudev_ functionality. You can enumerate devices, query device properties and
attributes or monitor devices, including asynchronous monitoring with threads,
or within the event loops of Qt, Glib or wxPython.

The binding supports CPython_ 3 and compatible versions of PyPy_.
It is tested against udev 151 or newer, earlier versions of udev
as found on dated Linux systems may work, but are not officially supported.


Usage
-----

Usage of pyudev is quite simply thanks to the power of the underlying udev
library. Getting the labels of all partitions just takes a few lines:

>>> import pyudev
>>> context = pyudev.Context()
>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...     print(device.get('ID_FS_LABEL', 'unlabeled partition'))
...
boot
swap
system

The website_ provides a detailed `user guide`_ and a complete `API reference`_.


Support
-------

Please report issues and questions to the issue tracker, but respect the
following guidelines:

- Check that the issue has not already been reported.
- Check that the issue is not already fixed in the ``master`` branch.
- Open issues with clear title and a detailed description in grammatically
  correct, complete sentences.
- Include the Python version and the udev version (see ``udevadm --version``) in
  the description of your issue.


Development
-----------

The source code is hosted on GitHub_::

   git clone git://github.com/pyudev/pyudev.git

Please fork the repository and send pull requests with your fixes or new
features, but respect the following guidelines:

- Read `how to properly contribute to open source projects on GitHub
  <http://gun.io/blog/how-to-github-fork-branch-and-pull-request/>`_.
- Understand the `branching model
  <http://nvie.com/posts/a-successful-git-branching-model/>`_.
- Write `good commit messages
  <http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html>`_.
- Add unit tests if possible (refer to the `testsuite documentation
  <http://pyudev.readthedocs.org/en/latest/tests/index.html>`_).
- Add API documentation in docstrings.
- Open a `pull request <https://help.github.com/articles/using-pull-requests>`_
  that relates to but one subject with a clear title and description in
  grammatically correct, complete sentences.


.. _LGPL: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
.. _Python: http://www.python.org/
.. _CPython: http://www.python.org/
.. _PyPy: http://www.pypy.org/
.. _libudev: https://www.freedesktop.org/software/systemd/man/libudev.html
.. _website: http://pyudev.readthedocs.org
.. _user guide: http://pyudev.readthedocs.org/en/latest/guide.html
.. _api reference: http://pyudev.readthedocs.org/en/latest/api/index.html
.. _issue tracker: http://github.com/pyudev/pyudev/issues
.. _GitHub: http://github.com/pyudev/pyudev
.. _git: http://www.git-scm.com/


Request a New Release
---------------------
I will be doing regular releases of this project every August and October,
shortly after Fedora releases are branched from rawhide.

If you believe an extra release would help you in some way, please file an
issue, explaining why you need the new release, and I expect I'll put one up.

Why should you explain why you need the new release? Well, it is helpful to me,
because pyudev is not at all part of my regular work, and I tend not to know
very much about how it is used these days.
07070100000006000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001200000000pyudev-0.24.1/doc07070100000007000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001D00000000pyudev-0.24.1/doc/_templates07070100000008000081A40000000000000000000000016443203100000350000000000000000000000000000000000000002700000000pyudev-0.24.1/doc/_templates/info.html{% macro link(title, link, internal=false) -%}
<a class="reference {{'internal' if internal else 'external'}}"
   href="{{ link }}">{{ title }}</a>
{%- endmacro %}

<p>pyudev {{release}}</p>

<h3>Install</h3>

<ul>
  <li><tt class="literal"><span class="pre">pip install pyudev</span></tt></li>
</ul>

<h3>Links</h3>
<ul>
  <li>pyudev on {{link('PyPI', 'https://pypi.python.org/pypi/pyudev')}} or
  {{link('Crate.io', 'https://crate.io/packages/pyudev')}}</li>
  <li>pyudev on {{link('Read the Docs', 'http://readthedocs.org/projects/pyudev/?fromdocs=pyudev')}}</li>
  <li>pyudev on {{link('GitHub', 'https://github.com/lunaryorn/pyudev')}}</li>
  <li>pyudev on {{link('Travis CI', 'http://travis-ci.org/lunaryorn/pyudev')}}</li>
</ul>

<h3>Support</h3>
<ul>
  <li>{{link('issue tracker', 'https://github.com/lunaryorn/pyudev/issues')}}</li>
</ul>
07070100000009000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001600000000pyudev-0.24.1/doc/api0707010000000A000081A4000000000000000000000001644320310000011C000000000000000000000000000000000000002000000000pyudev-0.24.1/doc/api/index.rstAPI documentation
=================

This document provides API reference documentation for pyudev.  Refer to the
:doc:`../guide` for an introduction into pyudev.

.. autosummary::
   :toctree: .

   pyudev
   pyudev.pyqt4
   pyudev.pyqt5
   pyudev.pyside
   pyudev.glib
   pyudev.wx
0707010000000B000081A4000000000000000000000001644320310000096E000000000000000000000000000000000000002600000000pyudev-0.24.1/doc/api/pyudev.glib.rst:mod:`pyudev.glib` – Glib/Gtk 2 integration
===========================================

.. automodule:: pyudev.glib
   :platform: Linux
   :synopsis: Glib integration

.. autoclass:: MonitorObserver

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: event_source

      The event source, which represents the watch on the :attr:`monitor`
      (as returned by :func:`glib.io_add_watch`), or ``None``, if
      :attr:`enabled` is ``False``.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following GObject signal:

   .. method:: device-event(observer, action, device)

      Emitted upon any device event.

      ``observer`` is the :class:`MonitorObserver`, which emitted the
      signal.  ``device`` is the :class:`~pyudev.Device`, which caused this
      event.

      Use :attr:`~pyudev.Device.action` to get the type of event.


Deprecated API
--------------

.. autoclass:: GUDevMonitorObserver

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: event_source

      The event source, which represents the watch on the :attr:`monitor`
      (as returned by :func:`glib.io_add_watch`), or ``None``, if
      :attr:`enabled` is ``False``.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following GObject signals:

   .. method:: device-event(observer, action, device)

      Emitted upon any device event.  ``observer`` is the
      :class:`GUDevMonitorObserver`, which emitted the signal.  ``action``
      is a unicode string containing the action name, and ``device`` is the
      :class:`~pyudev.Device`, which caused this event.

      Basically the last two arguments of this signal are simply the
      return value of :meth:`~pyudev.Monitor.receive_device`

   .. method:: device-added(observer, device)

      Emitted if a :class:`~pyudev.Device` is added (e.g a USB device was
      plugged).

   .. method:: device-removed(observer, device)

      Emitted if a :class:`~pyudev.Device` is removed (e.g. a USB device was
      unplugged).

   .. method:: device-changed(observer, device)

      Emitted if a :class:`~pyudev.Device` was somehow changed (e.g. a
      change of a property)

   .. method:: device-moved(observer, device)

      Emitted if a :class:`~pyudev.Device` was renamed, moved or
      re-parented.
0707010000000C000081A400000000000000000000000164432031000007FA000000000000000000000000000000000000002700000000pyudev-0.24.1/doc/api/pyudev.pyqt4.rst:mod:`pyudev.pyqt4` – PyQt4_ integration
========================================

.. automodule:: pyudev.pyqt4
   :platform: Linux
   :synopsis: PyQt4 integration

.. autoclass:: MonitorObserver

   .. automethod:: __init__

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: notifier

      The underlying :class:`QtCore.QSocketNotifier` used to watch the
      :attr:`monitor`.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following Qt signal:

   .. method:: deviceEvent(device)

      Emitted upon any device event.

      ``device`` is the :class:`~pyudev.Device` object describing the device.

      Use :attr:`~pyudev.Device.action` to get the type of event.


Deprecated API
--------------

.. autoclass:: QUDevMonitorObserver

   .. automethod:: __init__

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: notifier

      The underlying :class:`QtCore.QSocketNotifier` used to watch the
      :attr:`monitor`.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following Qt signals:

   .. method:: deviceEvent(action, device)

      Emitted upon any device event.  ``action`` is a unicode string
      containing the action name, and ``device`` is the
      :class:`~pyudev.Device` object describing the device.

      Basically the arguments of this signal are simply the return value of
      :meth:`~pyudev.Monitor.receive_device`

   .. method:: deviceAdded(device)

      Emitted if a :class:`~pyudev.Device` is added (e.g a USB device was
      plugged).

   .. method:: deviceRemoved(device)

      Emitted if a :class:`~pyudev.Device` is removed (e.g. a USB device was
      unplugged).

   .. method:: deviceChanged(device)

      Emitted if a :class:`~pyudev.Device` was somehow changed (e.g. a
      change of a property)

   .. method:: deviceMoved(device)

      Emitted if a :class:`~pyudev.Device` was renamed, moved or
      re-parented.
0707010000000D000081A40000000000000000000000016443203100000339000000000000000000000000000000000000002700000000pyudev-0.24.1/doc/api/pyudev.pyqt5.rst:mod:`pyudev.pyqt5` – PyQt5_ integration
========================================

.. automodule:: pyudev.pyqt5
   :platform: Linux
   :synopsis: PyQt5 integration

.. autoclass:: MonitorObserver

   .. automethod:: __init__

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: notifier

      The underlying :class:`QtCore.QSocketNotifier` used to watch the
      :attr:`monitor`.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following Qt signal:

   .. method:: deviceEvent(device)

      Emitted upon any device event.

      ``device`` is the :class:`~pyudev.Device` object describing the device.

      Use :attr:`~pyudev.Device.action` to get the type of event.



.. _PyQt5: http://riverbankcomputing.co.uk/software/pyqt/intro
0707010000000E000081A40000000000000000000000016443203100000800000000000000000000000000000000000000002800000000pyudev-0.24.1/doc/api/pyudev.pyside.rst:mod:`pyudev.pyside` – PySide_ integration
==========================================

.. automodule:: pyudev.pyside
   :platform: Linux
   :synopsis: PySide integration

.. autoclass:: MonitorObserver

   .. automethod:: __init__

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: notifier

      The underlying :class:`QtCore.QSocketNotifier` used to watch the
      :attr:`monitor`.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following Qt signal:

   .. method:: deviceEvent(device)

      Emitted upon any device event.

      ``device`` is the :class:`~pyudev.Device` object describing the device.

      Use :attr:`~pyudev.Device.action` to get the type of event.


Deprecated API
--------------

.. autoclass:: QUDevMonitorObserver

   .. automethod:: __init__

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. attribute:: notifier

      The underlying :class:`QtCore.QSocketNotifier` used to watch the
      :attr:`monitor`.

   .. autoattribute:: enabled

   .. rubric:: Signals

   This class emits the following Qt signals:

   .. method:: deviceEvent(action, device)

      Emitted upon any device event.  ``action`` is a unicode string
      containing the action name, and ``device`` is the
      :class:`~pyudev.Device` object describing the device.

      Basically the arguments of this signal are simply the return value of
      :meth:`~pyudev.Monitor.receive_device`

   .. method:: deviceAdded(device)

      Emitted if a :class:`~pyudev.Device` is added (e.g a USB device was
      plugged).

   .. method:: deviceRemoved(device)

      Emitted if a :class:`~pyudev.Device` is removed (e.g. a USB device was
      unplugged).

   .. method:: deviceChanged(device)

      Emitted if a :class:`~pyudev.Device` was somehow changed (e.g. a
      change of a property)

   .. method:: deviceMoved(device)

      Emitted if a :class:`~pyudev.Device` was renamed, moved or
      re-parented.
0707010000000F000081A4000000000000000000000001644320310000158F000000000000000000000000000000000000002100000000pyudev-0.24.1/doc/api/pyudev.rst:mod:`pyudev` - libudev binding
===============================

.. automodule:: pyudev
   :platform: Linux
   :synopsis: libudev bindings

.. autosummary::
   :nosignatures:

   Context
   Device
   Devices
   Monitor
   MonitorObserver


Version information
-------------------

.. data:: __version__

   The version of :mod:`pyudev` as string.  This string contains a major and a
   minor version number, and optionally a revision in the form
   ``major.minor.revision``.  As said, the ``revision`` part is optional and
   may not be present.

   This attribute is mainly intended for display purposes, use
   :data:`__version_info__` to check the version of :mod:`pyudev` in source
   code.

.. data:: __version_info__

   The version of :mod:`pyudev` as tuple of integers.  This tuple contains a
   major and a minor number, and optionally a revision number in the form
   ``(major, minor, revision)``.  As said, the ``revision`` component is
   optional and may not be present.

   .. versionadded:: 0.10

.. autofunction:: udev_version()


:class:`Context` – UDev database context
----------------------------------------

.. autoclass:: Context

   .. automethod:: __init__

   .. autoattribute:: sys_path

   .. autoattribute:: device_path

   .. autoattribute:: run_path

   .. autoattribute:: log_priority

   .. automethod:: list_devices


:class:`Enumerator` – device enumeration and filtering
------------------------------------------------------

.. autoclass:: Enumerator()

   .. automethod:: match

   .. automethod:: match_subsystem

   .. automethod:: match_sys_name

   .. automethod:: match_property

   .. automethod:: match_attribute

   .. automethod:: match_tag

   .. automethod:: match_parent

   .. automethod:: match_is_initialized

   .. automethod:: __iter__


:class:`Devices` – constructing `Device` objects
------------------------------------------------

.. autoclass:: Devices()

   .. rubric:: Construction of device objects

   .. automethod:: from_path

   .. automethod:: from_sys_path

   .. automethod:: from_name

   .. automethod:: from_device_number

   .. automethod:: from_device_file

   .. automethod:: from_environment

   .. automethod:: METHODS


:class:`Device` – accessing device information
----------------------------------------------

.. autoclass:: Device()

   .. rubric:: Construction of device objects

   .. automethod:: from_path

   .. automethod:: from_sys_path

   .. automethod:: from_name

   .. automethod:: from_device_number

   .. automethod:: from_device_file

   .. automethod:: from_environment

   .. rubric:: General attributes

   .. attribute:: context

      The :class:`Context` to which this device is bound.

      .. versionadded:: 0.5

   .. autoattribute:: sys_path

   .. autoattribute:: sys_name

   .. autoattribute:: sys_number

   .. autoattribute:: device_path

   .. autoattribute:: tags

   .. rubric:: Device driver and subsystem

   .. autoattribute:: subsystem

   .. autoattribute:: driver

   .. autoattribute:: device_type

   .. rubric:: Device nodes

   .. autoattribute:: device_node

   .. autoattribute:: device_number

   .. autoattribute:: device_links

   .. rubric:: Device initialization time

   .. autoattribute:: is_initialized

   .. autoattribute:: time_since_initialized

   .. rubric:: Device hierarchy

   .. autoattribute:: parent

   .. autoattribute:: ancestors

   .. autoattribute:: children

   .. automethod:: find_parent

   .. rubric:: Device events

   .. autoattribute:: action

   .. autoattribute:: sequence_number

   .. rubric:: Device properties

   .. automethod:: __iter__

   .. automethod:: __len__

   .. automethod:: __getitem__

   .. automethod:: asint

   .. automethod:: asbool

   .. rubric:: Sysfs attributes

   .. autoattribute:: attributes

   .. rubric:: Deprecated members

   .. automethod:: traverse

.. autoclass:: Attributes()

   .. attribute:: device

      The :class:`Device` to which these attributes belong.

   .. automethod:: __iter__

   .. automethod:: __len__

   .. automethod:: __getitem__

   .. automethod:: asstring

   .. automethod:: asint

   .. automethod:: asbool

.. autoclass:: Tags()

   .. automethod:: __iter__

   .. automethod:: __contains__


:class:`Device` exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~~

.. autoclass:: DeviceNotFoundError

.. autoclass:: DeviceNotFoundAtPathError
   :members:

.. autoclass:: DeviceNotFoundByNameError
   :members:

.. autoclass:: DeviceNotFoundByNumberError
   :members:

.. autoclass:: DeviceNotFoundInEnvironmentError
   :members:


:class:`Monitor` – device monitoring
------------------------------------

.. autoclass:: Monitor()

   .. automethod:: from_netlink

   .. attribute:: context

      The :class:`Context` to which this monitor is bound.

      .. versionadded:: 0.5

   .. autoattribute:: started

   .. automethod:: fileno

   .. automethod:: filter_by

   .. automethod:: filter_by_tag

   .. automethod:: remove_filter

   .. automethod:: start

   .. automethod:: set_receive_buffer_size

   .. automethod:: poll

   .. rubric:: Deprecated members

   .. automethod:: enable_receiving

   .. automethod:: receive_device

   .. automethod:: __iter__


:class:`MonitorObserver` – asynchronous device monitoring
---------------------------------------------------------

.. autoclass:: MonitorObserver

   .. attribute:: monitor

      Get the :class:`Monitor` observer by this object.

   .. automethod:: __init__

   .. automethod:: send_stop

   .. automethod:: stop
07070100000010000081A40000000000000000000000016443203100000A14000000000000000000000000000000000000002400000000pyudev-0.24.1/doc/api/pyudev.wx.rst:mod:`pyudev.wx` – wxPython_ integration
=====================================================

.. automodule:: pyudev.wx
   :platform: Linux
   :synopsis: wxWidgets integration


.. autoclass:: MonitorObserver

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. autoattribute::  enabled

.. rubric:: Events

:class:`MonitorObserver` posts the following event:

.. data:: EVT_DEVICE_EVENT

   Emitted upon any device event.  Receivers get a :class:`DeviceEvent` object
   as argument.

.. class:: DeviceEvent

   Argument object for :data:`EVT_DEVICE_EVENT`.

   .. attribute:: device

      The :class:`~pyudev.Device` object that caused this event.

      Use :attr:`~pyudev.Device.action` to get the type of event.

   .. rubric:: Deprecated members

   .. attribute:: action

      A unicode string containing the action name.

      .. deprecated:: 0.17
         Will be removed in 1.0.  Use :attr:`~pyudev.Device.action` instead.


Deprecated API
--------------

.. autoclass:: WxUDevMonitorObserver

   .. attribute:: monitor

      The :class:`~pyudev.Monitor` observed by this object.

   .. autoattribute::  enabled


.. rubric:: Events

:class:`WxUDevMonitorObserver` posts the following events in addition to
:data:`EVT_DEVICE_EVENT`:

.. data:: EVT_DEVICE_ADDED

   Emitted if a :class:`~pyudev.Device` is added (e.g a USB device was
   plugged).  Receivers get a :class:`DeviceAddedEvent` object as argument.

   .. deprecated:: 0.17
      Will be removed in 1.0.

.. data:: EVT_DEVICE_REMOVED

   Emitted if a :class:`~pyudev.Device` is removed (e.g. a USB device was
   unplugged).  Receivers get a :class:`DeviceRemovedEvent` object as argument.

   .. deprecated:: 0.17
      Will be removed in 1.0.

.. data:: EVT_DEVICE_CHANGED

   Emitted if a :class:`~pyudev.Device` was somehow changed (e.g. a change of a
   property).  Receivers get a :class:`DeviceChangedEvent` object as argument.

   .. deprecated:: 0.17
      Will be removed in 1.0.

.. data:: EVT_DEVICE_MOVED

   Emitted if a :class:`~pyudev.Device` was renamed, moved or re-parented.
   Receivers get a :class:`DeviceMovedEvent` object as argument.

.. class:: DeviceAddedEvent
           DeviceRemovedEvent
           DeviceChangedEvent
           DeviceMovedEvent

   Argument objects for :data:`EVT_DEVICE_ADDED`, :data:`EVT_DEVICE_REMOVED`,
   :data:`EVT_DEVICE_CHANGED` and :data:`EVT_DEVICE_MOVED`.

   .. deprecated:: 0.17
      Will be removed in 1.0.

   .. attribute:: device

      The :class:`~pyudev.Device` object that caused this event.
07070100000011000081A40000000000000000000000016443203100000031000000000000000000000000000000000000001E00000000pyudev-0.24.1/doc/changes.rstChangelog
*********

.. include:: ../CHANGES.rst
07070100000012000081A4000000000000000000000001644320310000104C000000000000000000000000000000000000001A00000000pyudev-0.24.1/doc/conf.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

import sys
import os

from docutils import nodes
from docutils.parsers.rst import Directive

# add the pyudev source directory to our path
doc_directory = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.normpath(os.path.join(doc_directory, os.pardir)))
# add the tests directory to our path to point autodoc on the testsuite plugins
sys.path.append(os.path.normpath(os.path.join(doc_directory, os.pardir, "tests")))


class Mock(object):
    """
    Mock modules.

    Taken from
    http://read-the-docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules
    with some slight changes.
    """

    @classmethod
    def mock_modules(cls, *modules):
        for module in modules:
            sys.modules[module] = cls()

    def __init__(self, *args, **kwargs):
        pass

    def __call__(self, *args, **kwargs):
        return self.__class__()

    def __getattr__(self, attribute):
        if attribute in ("__file__", "__path__"):
            return os.devnull
        else:
            # return the *class* object here.  Mocked attributes may be used as
            # base class in pyudev code, thus the returned mock object must
            # behave as class, or else Sphinx autodoc will fail to recognize
            # the mocked base class as such, and "autoclass" will become
            # meaningless
            return self.__class__


# mock out native modules used throughout pyudev to enable Sphinx autodoc even
# if these modules are unavailable, as on readthedocs.org
Mock.mock_modules(
    "PyQt5",
    "PyQt5.QtCore",
    "PyQt4",
    "PyQt4.QtCore",
    "PySide",
    "PySide.QtCore",
    "glib",
    "gobject",
    "wx",
    "wx.lib",
    "wx.lib.newevent",
    "pyudev._libudev",
)


# mock out the NewEvent function of wxPython.  Let's praise the silly wx API
def NewEventMock():
    yield "event_class"
    yield "event_constant"


sys.modules["wx.lib.newevent"].NewEvent = NewEventMock


import pyudev

needs_sphinx = "1.0"

extensions = ["sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.intersphinx"]

master_doc = "index"
exclude_patterns = ["_build/*"]
source_suffix = ".rst"

project = "pyudev"
copyright = "2010, 2011 Sebastian Wiesner"
version = ".".join(pyudev.__version__.split(".")[:2])
release = pyudev.__version__

templates_path = ["_templates"]
html_theme = "classic"
html_static_path = []
html_sidebars = {
    "**": ["info.html", "localtoc.html", "relations.html", "sourcelink.html"]
}

intersphinx_mapping = {
    "python": ("http://docs.python.org/", None),
    "pytest": ("http://pytest.org/latest", None),
}


class UDevVersion(Directive):
    """
    Directive to document the minimum udev version to use an attribute or
    method
    """

    has_content = False
    required_arguments = 1
    option_spec = {}

    def run(self):
        udevversion = self.arguments[0]
        para = nodes.paragraph(udevversion, "", classes=["udevversion"])
        text = "Required udev version: {0}".format(*self.arguments)
        node = nodes.inline(udevversion, text, classes=["versionmodified"])
        para.append(node)
        return [para]


def setup(app):
    from sphinx.ext.autodoc import cut_lines

    app.connect("autodoc-process-docstring", cut_lines(2, what=["module"]))
    app.add_directive("udevversion", UDevVersion)
07070100000013000081A400000000000000000000000164432031000005E6000000000000000000000000000000000000002100000000pyudev-0.24.1/doc/contribute.rstContribute
==========

Please fork the repository, and send pull requests with new features or bug
fixes, but respect the following guidelines:

- Read `how to properly contribute to open source projects on GitHub
  <contribute>`_.
- Understand the `branching model <branching>`_.
- Use a topic branch based on the ``develop`` branch to easily amend a pull
  request later, if necessary.
- Write `good commit messages <commits>`_.
- Squash commits on the topic branch before opening a pull request.
- Respect :pep:`8` (use pep8_ to check your coding style compliance).
- Add unit tests if possible (refer to the :doc:`testsuite documentation
  <tests/index>`).
- Add API documentation in docstrings.
- Open a `pull request`_.
  that relates to but one subject with a clear title and description in
  grammatically correct, complete sentences.

Complying to these guidelines greatly increase the change of getting your pull
request merged.  You will be asked to improve your changeset if your pull
request breaks any of the above guidelines.

If you intend to make larger changes, especially if these changes break the ABI,
please ask on the mailing list first.


.. _pep8: http://pypi.python.org/pypi/pep8/
.. _contribute: http://gun.io/blog/how-to-github-fork-branch-and-pull-request/
.. _branching: http://nvie.com/posts/a-successful-git-branching-model/
.. _commits: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
.. _pull request: https://help.github.com/articles/using-pull-requests
07070100000014000081A400000000000000000000000164432031000001C7000000000000000000000000000000000000002300000000pyudev-0.24.1/doc/endorsements.rstpyudev Users
============

If you are using pyudev and would like the world to know how and why, here is
the place. Just submit a PR with an addition to the documentation, something
like:

--------------------------------------------------------------------------------
Choice of information about yourself.
--------------------------------------------------------------------------------
What you are doing with pyudev and why it beats the alternatives.
07070100000015000081A4000000000000000000000001644320310000386B000000000000000000000000000000000000001C00000000pyudev-0.24.1/doc/guide.rstUser guide
==========

.. currentmodule:: pyudev

This guide gives an introduction in how to use pyudev for common operations
like device enumeration or monitoring:

.. contents::

A detailed reference is provided in the :doc:`API documentation <api/index>`.


Getting started
---------------

Import pyudev and verify that you're using the latest version:

>>> import pyudev
>>> pyudev.__version__
u'0.16'
>>> pyudev.udev_version()
181

This prints the version of pyudev itself and of the underlying libudev_.


A note on versioning
--------------------

pyudev supports libudev_ 151 or newer, but still tries to cover the most recent
libudev_ API completely.  If you are using older libudev_ releases, some
functionality of pyudev may be unavailable, simply because libudev_ is too old
to support a specific feature.  Whenever this is the case, the minimum required
version of udev is noted in the documentation (see
:attr:`Device.is_initialized` for an example).  If no version is specified for
an attribute or a method, it is available on all supported libudev_ versions.
You can check the version of the underlying libudev_ with
:func:`pyudev.udev_version()`.


Enumerating devices
-------------------

A common use case is to enumerate available devices, or a subset thereof.  But
before you can do anything with pyudev, you need to establish a "connection" to
the udev device database first.  This connection is represented by a library
:class:`Context`:

>>> context = pyudev.Context()

The :class:`Context` is the central object of pyudev and libudev_.  You will
need a :class:`Context` object for almost anything in pyudev.  With the
``context`` you can now enumerate the available devices:

>>> for device in context.list_devices(): # doctest: +ELLIPSIS
...     device
...
Device(u'/sys/devices/LNXSYSTM:00')
Device(u'/sys/devices/LNXSYSTM:00/LNXCPU:00')
Device(u'/sys/devices/LNXSYSTM:00/LNXCPU:01')
...

By default, :meth:`list_devices()` yields all devices available on the system
as :class:`Device` objects, but you can filter the list of devices with keyword
arguments to enumerate all available partitions for example:

>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...    print(device)
...
Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda/sda1')
Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda/sda2')
Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda/sda3')

The choice of the right filters depends on the use case and generally requires
some knowledge about how udev classifies and categorizes devices.  This is out
of the scope of this guide.  Poke around in ``/sys/`` to get a feeling for the
udev-way of device handling, read the udev documentation or one of the
tutorials in the net.

The keyword arguments of :meth:`list_devices()` provide the most common filter
operations.  You can apply other, less common filters by calling one of the
``match_*`` methods on the :class:`Enumerator` returned by of
:meth:`list_devices()`.


Accessing individual devices directly
-------------------------------------

If you just need a single specific :class:`Device`, you don't need to enumerate
all devices with a specific filter criterion.  Instead, you can directly create
:class:`Device` objects from a device path (:meth:`Devices.from_path()`), by
from a subsystem and device name (:meth:`Devices.from_name()`) or from a device
file (:meth:`Devices.from_device_file()`).  The following code gets the
:class:`Device` object for the first hard disc in three different ways:

>>> pyudev.Devices.from_path(context, '/sys/block/sda')
Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda')
>>> pyudev.Devices.from_name(context, 'block', 'sda')
Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda')
>>> pyudev.Devices.from_device_file(context, '/dev/sda')
Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda')

As you can see, you need to pass a :class:`Context` to both methods as
reference to the udev database from which to retrieve information about the
device.

.. note::

   The :class:`Device` objects created in the above example refer to the same
   device.  Consequently, they are considered equal:

   >>> pyudev.Devices.from_path(context, '/sys/block/sda') == pyudev.Devices.from_name(context, 'block', 'sda')
   True

   Whereas :class:`Device` objects referring to different devices are unequal:

   >>> pyudev.Devices.from_name(context, 'block', 'sda') == pyudev.Devices.from_name(context, 'block', 'sda1')
   False


Querying device information
---------------------------

As you've seen, :class:`Device` represents a device in the udev database.  Each
such device has a set of "device properties" (not to be confused with Python
properties as created by :func:`property()`!) that describe the capabilities
and features of this device as well as its relationship to other devices.

Common device properties are also available as properties of a :class:`Device`
object.  For instance, you can directly query the :attr:`device_node` and the
:attr:`device_type` of block devices:

>>> for device in context.list_devices(subsystem='block'):
...     print('{0} ({1})'.format(device.device_node, device.device_type))
...
/dev/sr0 (disk)
/dev/sda (disk)
/dev/sda1 (partition)
/dev/sda2 (partition)
/dev/sda3 (partition)

For other udev properties, :class:`Device` provides a mapping interface
to access the device properties by means of its properties attribute.

>>> for device in context.list_devices(subsystem='block'):
...    print('{0} ({1})'.format(device.properties['DEVNAME'], device.properties['DEVTYPE']))
...
/dev/sr0 (disk)
/dev/sda (disk)
/dev/sda1 (partition)
/dev/sda2 (partition)
/dev/sda3 (partition)

.. warning::

   When filtering devices, you have to use the device property names.  The
   names of corresponding properties of :class:`Device` will generally **not**
   work.  Compare the following two statements:

   >>> [device.device_node for device in context.list_devices(subsystem='block', DEVTYPE='partition')]
   [u'/dev/sda1', u'/dev/sda2', u'/dev/sda3']
   >>> [device.device_node for device in context.list_devices(subsystem='block', device_type='partition')]
   []


But you can also query many device properties that are not available as Python
properties on the :class:`Device` object with a convenient mapping interface,
like the filesystem type.  :class:`Device` provides a convenient mapping
interface for this purpose:

>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...     print('{0} ({1})'.format(device.device_node, device.properties.get('ID_FS_TYPE')))
...
/dev/sda1 (ext3)
/dev/sda2 (swap)
/dev/sda3 (ext4)

.. note::

   Such device specific properties may not be available on devices.  Either use
   ``get()`` to specify default values for missing properties, or be prepared
   to catch :exc:`~exceptions.KeyError`.

Most device properties are computed by udev rules from the driver- and
device-specific "device attributes".  The :attr:`Device.attributes` mapping
gives you access to these attributes, but generally you should not need these.
Use the device properties whenever possible.


Examing the device hierarchy
----------------------------

A :class:`Device` is part of a device hierarchy, and can have a
:attr:`~Device.parent` device that more or less resembles the physical
relationship between devices.  For instance, the :attr:`~Device.parent` of
partition devices is a :class:`Device` object that represents the disc the
partition is located on:

>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...    print('{0} is located on {1}'.format(device.device_node, device.parent.device_node))
...
/dev/sda1 is located on /dev/sda
/dev/sda2 is located on /dev/sda
/dev/sda3 is located on /dev/sda

Generally, you should not rely on the direct parent-child relationship between
two devices.  Instead of accessing the parent directly, search for a parent
within a specific subsystem, e.g. for the parent ``block`` device, with
:meth:`~Device.find_parent()`:

>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...    print('{0} is located on {1}'.format(device.device_node, device.find_parent('block').device_node))
...
/dev/sda1 is located on /dev/sda
/dev/sda2 is located on /dev/sda
/dev/sda3 is located on /dev/sda

This also save you the tedious work of traversing the device tree manually, if
you are interested in grand parents, like the name of the PCI slot of the SCSI
or IDE controller of the disc that contains a partition:

>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...    print('{0} attached to PCI slot {1}'.format(device.device_node, device.find_parent('pci')['PCI_SLOT_NAME']))
...
/dev/sda1 attached to PCI slot 0000:00:0d.0
/dev/sda2 attached to PCI slot 0000:00:0d.0
/dev/sda3 attached to PCI slot 0000:00:0d.0


Monitoring devices
------------------

Synchronous monitoring
~~~~~~~~~~~~~~~~~~~~~~

The Linux kernel emits events whenever devices are added, removed (e.g. a USB
stick was plugged or unplugged) or have their attributes changed (e.g. the
charge level of the battery changed).  With :class:`pyudev.Monitor` you can
react on such events, for example to react on added or removed mountable
filesystems:

>>> monitor = pyudev.Monitor.from_netlink(context)
>>> monitor.filter_by('block')
>>> for device in iter(monitor.poll, None):
...     if 'ID_FS_TYPE' in device:
...         print('{0} partition {1}'.format(device.action, device.get('ID_FS_LABEL')))
...
add partition MULTIBOOT
remove partition MULTIBOOT

After construction of a monitor, you can install an event filter on the monitor
using :meth:`~Monitor.filter_by()`.  In the above example only events from the
``block`` subsystem are handled.

.. note::

   Always prefer :meth:`~Monitor.filter_by()` and
   :meth:`~Monitor.filter_by_tag()` over manually filtering devices (e.g. by
   ``device.subsystem == 'block'`` or ``tag in device.tags``).  These methods
   install the filter on the *kernel side*.  A process waiting for events is
   thus only woken up for events that match these filters.  This is much nicer
   in terms of power consumption and system load than executing filters in the
   process itself.

Eventually, you can receive events from the monitor.  As you can see, a
:class:`Monitor` is iterable and synchronously yields occurred events.  If you
iterate over a :class:`Monitor`, you will synchronously receive events in an
endless loop, until you raise an exception, or ``break`` the loop.

This is the quick and dirty way of monitoring, suitable for small scripts or
quick experiments.  In most cases however, simply iterating over the monitor is
not sufficient, because it blocks the main thread, and can only be stopped if
an event occurs (otherwise the loop is not entered and you have no chance to
``break`` it).


Asynchronous monitoring
~~~~~~~~~~~~~~~~~~~~~~~

For such use cases, pyudev provides asynchronous monitoring with
:class:`MonitorObserver`.  You can use it to log added and removed mountable
filesystems to a file, for example:

>>> monitor = pyudev.Monitor.from_netlink(context)
>>> monitor.filter_by('block')
>>> def log_event(device):
...    if 'ID_FS_TYPE' in device.properties:
...        with open('filesystems.log', 'a+') as stream:
...            print('{0} - {1}'.format(device.action, device.get('ID_FS_LABEL')), file=stream)
...
>>> observer = pyudev.MonitorObserver(monitor, callback=log_event)
>>> observer.start()

The ``observer`` gets a callback (``log_event()`` in this case) which is
asynchronously invoked on every event emitted by the underlying ``monitor``
after the observer has been started using :meth:`~threading.Thread.start()`.

.. warning::

   The callback is invoked from a *different* thread than the one in which the
   ``observer`` was created.  Be sure to protect access to shared resource
   properly when you access them from the callback (e.g. by locking).

The ``observer`` can be stopped at any moment using :meth:`~MonitorObserver.stop()``:

>>> observer.stop()

.. warning::

   Do *not* call :meth:`~MonitorObserver.stop()` from the event handler,
   neither directly nor indirectly.  Use :meth:`~MonitorObserver.send_stop()`
   if you need to stop monitoring from inside the event handler.


GUI toolkit integration
~~~~~~~~~~~~~~~~~~~~~~~

If you're using a GUI toolkit, you already have the event system of the GUI
toolkit at hand.  pyudev provides observer classes that seamlessly integration
in the event system of the GUI toolkit and relieve you from caring with
synchronisation issues that would occur with thread-based monitoring as
implemented by :class:`MonitorObserver`.

pyudev supports all major GUI toolkits available for Python:

- Qt_ 5 using :mod:`pyudev.pyqt5`
- Qt_ 4 using :mod:`pyudev.pyqt4` for the PyQt4_ binding or :mod:`pyudev.pyside`
  for the PySide_ binding
- PyGtk_ 2 using :mod:`pyudev.glib`
- wxWidgets_ and wxPython_ using :mod:`pyudev.wx`

Each of these modules provides an observer class that observers the monitor
asynchronously and emits proper signals upon device events.

For instance, the above example would look like this in a PySide_ application:

>>> from pyudev.pyside import QUDevMonitorObserver
>>> monitor = pyudev.Monitor.from_netlink(context)
>>> observer = QUDevMonitorObserver(monitor)
>>> observer.deviceEvent.connect(log_event)
>>> monitor.start()

Device objects as booleans
~~~~~~~~~~~~~~~~~~~~~~~~~~
The use of a Device object in a boolean context as a shorthand for a comparison
with None is an error.

The Device class inherits from the abstract Mapping class, as it maps udev
property names to their values. Consequently, if a Device object has no udev
properties, an unusual but not impossible occurance, the object is
interpreted as False in a boolean context.

.. _pypi: https://pypi.python.org/pypi/pyudev
.. _libudev: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/
.. _Qt: http://qt.io/developers/
.. _PyQt5: https://riverbankcomputing.co.uk/software/pyqt/intro
.. _PyQt4: https://riverbankcomputing.co.uk/software/pyqt/intro
.. _PySide: http://wiki.qt.io/PySide
.. _PyGtk: http://www.pygtk.org/
.. _wxWidgets: http://wxwidgets.org
.. _wxPython: http://www.wxpython.org
07070100000016000081A40000000000000000000000016443203100000A74000000000000000000000000000000000000001C00000000pyudev-0.24.1/doc/index.rstpyudev -- pure Python libudev_ binding
======================================

pyudev |release| (:doc:`changes`, :doc:`installation <install>`)

pyudev is a :doc:`LGPL licenced <licencing>`, pure Python_ 3 binding to
libudev_, the device and hardware management and information library of Linux.

Almost the complete libudev_ functionality is exposed. You can:

* Enumerate devices, filtered by specific criteria (:class:`pyudev.Context`)
* Query device information, properties and attributes,
* Monitor devices, both synchronously and asynchronously with background
  threads, or within the event loops of Qt (:mod:`pyudev.pyqt5`, :mod:`pyudev.pyqt4`,
  :mod:`pyudev.pyside`), glib (:mod:`pyudev.glib`) and wxPython
  (:mod:`pyudev.wx`).


Documentation
-------------

Thanks to the power of libudev_, usage of pyudev is very simple.  Getting the
labels of all partitions just takes a few lines:

>>> import pyudev
>>> context = pyudev.Context()
>>> for device in context.list_devices(subsystem='block', DEVTYPE='partition'):
...     print(device.get('ID_FS_LABEL', 'unlabeled partition'))
...
boot
swap
system

A user guide gives an introduction into common operations and concepts of
pyudev, the API documentation provides a detailed reference:

.. toctree::
   :maxdepth: 2

   install
   guide
   api/index


Support
-------

Please report issues, bugs and questions to the `issue tracker`_, but respect
the following guidelines:

- Check that the issue has not already been reported.
- Check that the issue is not already fixed in the ``master`` branch.
- Open issues with clear title and a detailed description in grammatically
  correct, complete sentences.
- Include the Python version and the udev version (see ``udevadm --version``) in
  the description of your issue.


.. _development:

Development
-----------

The source code is hosted on GitHub_::

   git clone https://github.com/lunaryorn/pyudev.git

If you want to contribute to pyudev, please read the guidelines for
contributions and the testsuite documentation.

.. toctree::
   :maxdepth: 2

   contribute
   tests/index


Endorsements
------------
If you're using pyudev and want to say something about it please add yourself
to the endorsements page.

.. toctree::
   :maxdepth: 1

   endorsements


Other reading
-------------

.. toctree::
   :maxdepth: 1

   changes
   licencing


.. _Python: http://www.python.org/
.. _libudev: http://www.freedesktop.org/software/systemd/man/libudev.html
.. _librelist.com: http://librelist.com/
.. _list archives: http://librelist.com/browser/pyudev/
.. _issue tracker: https://github.com/lunaryorn/pyudev/issues
.. _GitHub: https://github.com/lunaryorn/pyudev
07070100000017000081A400000000000000000000000164432031000004A2000000000000000000000000000000000000001E00000000pyudev-0.24.1/doc/install.rstInstallation
============

Python versions and implementations
-----------------------------------

pyudev supports CPython 3 and compatible versions of PyPy.


Dependencies
------------

pyudev needs libudev 151 and newer, earlier versions of libudev as found on
dated Linux systems may work, but are not tested and not officially supported.
It is written in pure Python based on :mod:`ctypes`, so no compilers or headers
are required for installation.

To use any of the toolkit integration modules. the corresponding toolkit must be
available, but no toolkit is required during installation.


Installation from Cheeseshop
----------------------------

Install pyudev from the Cheeseshop_ with pip_::

   pip install pyudev


Installation from source code
-----------------------------

Close the public repository::

   git clone https://github.com/lunaryorn/pyudev.git

Or download `tarball <https://github.com/lunaryorn/pyudev/tarball/master>`_::

   curl -OL https://github.com/lunaryorn/pyudev/tarball/master

Then install pyudev from the source code tree::

   python setup.py install


.. _Cheeseshop: http://pypi.python.org/pypi/pyudev
.. _pip: http://www.pip-installer.org/
07070100000018000081A40000000000000000000000016443203100000034000000000000000000000000000000000000002000000000pyudev-0.24.1/doc/licencing.rstLicencing
=========

.. literalinclude:: ../COPYING
07070100000019000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001800000000pyudev-0.24.1/doc/tests0707010000001A000081A400000000000000000000000164432031000001C5000000000000000000000000000000000000002200000000pyudev-0.24.1/doc/tests/index.rstTestsuite documentation
=======================

This document explains the pyudev test suite and how to add new tests to this
suite.

The pyudev testsuite uses the powerful pytest_ unittest framework, accompied by
the nice mock_ library for mocking native functions and heavily extended with
plugins to support the tests.


.. toctree::

   running.rst
   plugins.rst


.. _pytest: http://pytest.org
.. _mock: http://www.voidspace.org.uk/python/mock/

0707010000001B000081A400000000000000000000000164432031000005B8000000000000000000000000000000000000002400000000pyudev-0.24.1/doc/tests/plugins.rst:mod:`plugins` – Testsuite plugins
==================================

.. automodule:: plugins

The following plugins are provided and enabled:

.. autosummary::

   .fake_monitor
   .mock_libudev
   .travis


:mod:`pytest` namespace
~~~~~~~~~~~~~~~~~~~~~~~

The plugin adds the following functions to the :mod:`pytest` namespace:

.. autofunction:: load_dummy

.. autofunction:: unload_dummy


:mod:`~plugins.fake_monitor` – A fake :class:`Monitor`
------------------------------------------------------

.. automodule:: plugins.fake_monitor

.. autoclass:: FakeMonitor
   :members:


Funcargs
~~~~~~~~

The plugin provides the following :ref:`funcargs <funcargs>`:

.. autofunction:: fake_monitor


:mod:`~plugins.mock_libudev` – Mock calls to libudev
----------------------------------------------------

.. automodule:: plugins.mock_libudev

.. autofunction:: libudev_list(function, items)


:mod:`~plugins.travis` – Support for Travis CI
----------------------------------------------

.. automodule:: plugins.travis


Test markers
~~~~~~~~~~~~

.. attribute:: pytest.mark.not_on_travis

   Do not run the decorated test on Travis CI::

      @pytest.mark.not_on_travis
      def test_foo():
          assert True

   ``test_foo`` will not be run on Travis CI.


:mod:`pytest` namespace
~~~~~~~~~~~~~~~~~~~~~~~

The plugin adds the following functions to the :mod:`pytest` namespace:

.. autofunction:: is_on_travis_ci


.. _pytest: http://pytest.org
0707010000001C000081A4000000000000000000000001644320310000022B000000000000000000000000000000000000002400000000pyudev-0.24.1/doc/tests/running.rstTest running
============
See the current CI configuration file to determine what tests are
currently being run and how to run them.

Device samples
~~~~~~~~~~~~~~

Many pyudev tests run against the real device database of the system the tests
are executed on.  As testing against the whole database takes a long time,
tests are run against a random sample by default.  With the command line
options provided by :mod:`~tests.plugins.udev_database` you can configure the
size of this sample, or run the tests against a single device or the whole
database.
0707010000001D000081A40000000000000000000000016443203100000057000000000000000000000000000000000000001900000000pyudev-0.24.1/pytest.ini[tool:pytest]
# do not search for tests in build directory
norecursedirs = .* _* build
0707010000001E000081A400000000000000000000000164432031000009AE000000000000000000000000000000000000001700000000pyudev-0.24.1/setup.py#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""
setup.py
"""

# isort: STDLIB
import os

# isort: THIRDPARTY
import setuptools


def local_file(name):
    """
    Function to obtain the relative path of a filename.
    """
    return os.path.relpath(os.path.join(os.path.dirname(__file__), name))


README = local_file("README.rst")

with open(local_file("src/pyudev/version.py"), encoding="utf-8") as o:
    exec(o.read())  # pylint: disable=exec-used

with open(local_file("README.rst"), encoding="utf-8") as o:
    long_description = o.read()

setuptools.setup(
    name="pyudev",
    version=__version__,  # pylint: disable=undefined-variable
    url="http://pyudev.readthedocs.org/",
    author="Sebastian Wiesner",
    author_email="lunaryorn@gmail.com",
    description="A libudev binding",
    long_description=long_description,
    long_description_content_type="text/x-rst",
    platforms=["Linux"],
    license="LGPL 2.1+",
    python_requires=">=3.7",
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
        "Operating System :: POSIX :: Linux",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: Implementation :: CPython",
        "Programming Language :: Python :: Implementation :: PyPy",
        "Topic :: Software Development :: Libraries",
        "Topic :: System :: Hardware",
        "Topic :: System :: Operating System Kernels :: Linux",
    ],
    package_dir={"": "src"},
    packages=setuptools.find_packages("src"),
)
0707010000001F000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001200000000pyudev-0.24.1/src07070100000020000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001900000000pyudev-0.24.1/src/pyudev07070100000021000081A400000000000000000000000164432031000007E7000000000000000000000000000000000000002500000000pyudev-0.24.1/src/pyudev/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev
    ======

    A binding to libudev.

    The :class:`Context` provides the connection to the udev device database
    and enumerates devices.  Individual devices are represented by the
    :class:`Device` class.

    Device monitoring is provided by :class:`Monitor` and
    :class:`MonitorObserver`.  With :mod:`pyudev.pyqt4`, :mod:`pyudev.pyside`,
    :mod:`pyudev.glib` and :mod:`pyudev.wx` device monitoring can be integrated
    into the event loop of various GUI toolkits.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: LOCAL
from pyudev._errors import (
    DeviceNotFoundAtPathError,
    DeviceNotFoundByFileError,
    DeviceNotFoundByNameError,
    DeviceNotFoundByNumberError,
    DeviceNotFoundError,
    DeviceNotFoundInEnvironmentError,
)
from pyudev._util import udev_version
from pyudev.core import Context, Enumerator
from pyudev.device import Attributes, Device, Devices, Tags
from pyudev.discover import (
    DeviceFileHypothesis,
    DeviceNameHypothesis,
    DeviceNumberHypothesis,
    DevicePathHypothesis,
    Discovery,
)
from pyudev.monitor import Monitor, MonitorObserver
from pyudev.version import __version__, __version_info__
07070100000022000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000002400000000pyudev-0.24.1/src/pyudev/_ctypeslib07070100000023000081A400000000000000000000000164432031000003C1000000000000000000000000000000000000003000000000pyudev-0.24.1/src/pyudev/_ctypeslib/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._ctypeslib
    =================

    Wrappers for libraries.

    .. moduleauthor::  mulhern  <amulhern@redhat.com>
"""

from . import libc, libudev
07070100000024000081A40000000000000000000000016443203100000C0D000000000000000000000000000000000000003600000000pyudev-0.24.1/src/pyudev/_ctypeslib/_errorcheckers.py# -*- coding: utf-8 -*-
# Copyright (C) 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._ctypeslib._errorcheckers
    ================================

    Error checkers for ctypes wrappers.
"""

# isort: STDLIB
import errno
import os
from ctypes import get_errno

ERRNO_EXCEPTIONS = {
    errno.ENOMEM: MemoryError,
    errno.EOVERFLOW: OverflowError,
    errno.EINVAL: ValueError,
}


def exception_from_errno(errnum):
    """Create an exception from ``errnum``.

    ``errnum`` is an integral error number.

    Return an exception object appropriate to ``errnum``.

    """
    exception = ERRNO_EXCEPTIONS.get(errnum)
    errorstr = os.strerror(errnum)
    if exception is None:
        return EnvironmentError(errnum, errorstr)
    return exception(errorstr)


def check_negative_errorcode(result, _func, *_args):
    """Error checker for funtions, which return negative error codes.

    If ``result`` is smaller than ``0``, it is interpreted as negative error
    code, and an appropriate exception is raised:

    - ``-ENOMEM`` raises a :exc:`~exceptions.MemoryError`
    - ``-EOVERFLOW`` raises a :exc:`~exceptions.OverflowError`
    - all other error codes raise :exc:`~exceptions.EnvironmentError`

    If result is greater or equal to ``0``, it is returned unchanged.

    """
    if result < 0:
        # udev returns the *negative* errno code at this point
        errnum = -result
        raise exception_from_errno(errnum)
    else:
        return result


def check_errno_on_nonzero_return(result, _func, *_args):
    """Error checker to check the system ``errno`` as returned by
    :func:`ctypes.get_errno()`.

    If ``result`` is not ``0``, an exception according to this errno is raised.
    Otherwise nothing happens.

    """
    if result != 0:
        errnum = get_errno()
        if errnum != 0:
            raise exception_from_errno(errnum)
    return result


def check_errno_on_null_pointer_return(result, _func, *_args):
    """Error checker to check the system ``errno`` as returned by
    :func:`ctypes.get_errno()`.

    If ``result`` is a null pointer, an exception according to this errno is
    raised.  Otherwise nothing happens.

    """
    # pylint: disable=invalid-name
    if not result:
        errnum = get_errno()
        if errnum != 0:
            raise exception_from_errno(errnum)
    return result
07070100000025000081A400000000000000000000000164432031000004B5000000000000000000000000000000000000002C00000000pyudev-0.24.1/src/pyudev/_ctypeslib/libc.py# -*- coding: utf-8 -*-
# Copyright (C) 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._ctypeslib.libc
    ======================

    Wrappers for libc.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
from ctypes import c_int

from ._errorcheckers import check_errno_on_nonzero_return

FD_PAIR = c_int * 2

SIGNATURES = dict(
    pipe2=([FD_PAIR, c_int], c_int),
)

ERROR_CHECKERS = dict(
    pipe2=check_errno_on_nonzero_return,
)
07070100000026000081A4000000000000000000000001644320310000296A000000000000000000000000000000000000002F00000000pyudev-0.24.1/src/pyudev/_ctypeslib/libudev.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._ctypeslib.libudev
    =========================

    Wrapper types for libudev.  Use ``libudev`` attribute to access libudev
    functions.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
from ctypes import POINTER, Structure, c_char, c_char_p, c_int, c_uint, c_ulonglong

from ._errorcheckers import (
    check_errno_on_nonzero_return,
    check_errno_on_null_pointer_return,
    check_negative_errorcode,
)


class udev(Structure):  # pylint: disable=invalid-name
    """
    Dummy for ``udev`` structure.
    """

    # pylint: disable=too-few-public-methods
    pass


udev_p = POINTER(udev)  # pylint: disable=invalid-name


class udev_enumerate(Structure):  # pylint: disable=invalid-name
    """
    Dummy for ``udev_enumerate`` structure.
    """

    # pylint: disable=too-few-public-methods
    pass


udev_enumerate_p = POINTER(udev_enumerate)  # pylint: disable=invalid-name


class udev_list_entry(Structure):  # pylint: disable=invalid-name
    """
    Dummy for ``udev_list_entry`` structure.
    """

    # pylint: disable=too-few-public-methods
    pass


udev_list_entry_p = POINTER(udev_list_entry)  # pylint: disable=invalid-name


class udev_device(Structure):  # pylint: disable=invalid-name
    """
    Dummy for ``udev_device`` structure.
    """

    # pylint: disable=too-few-public-methods
    pass


udev_device_p = POINTER(udev_device)  # pylint: disable=invalid-name


class udev_monitor(Structure):  # pylint: disable=invalid-name
    """
    Dummy for ``udev_device`` structure.
    """

    # pylint: disable=too-few-public-methods
    pass


udev_monitor_p = POINTER(udev_monitor)  # pylint: disable=invalid-name


class udev_hwdb(Structure):  # pylint: disable=invalid-name
    """
    Dummy for ``udev_hwdb`` structure.
    """

    # pylint: disable=too-few-public-methods
    pass


udev_hwdb_p = POINTER(udev_hwdb)  # pylint: disable=invalid-name

dev_t = c_ulonglong  # pylint: disable=invalid-name

SIGNATURES = dict(
    # context
    udev_new=([], udev_p),
    udev_unref=([udev_p], None),
    udev_ref=([udev_p], udev_p),
    udev_get_sys_path=([udev_p], c_char_p),
    udev_get_dev_path=([udev_p], c_char_p),
    udev_get_run_path=([udev_p], c_char_p),
    udev_get_log_priority=([udev_p], c_int),
    udev_set_log_priority=([udev_p, c_int], None),
    udev_enumerate_new=([udev_p], udev_enumerate_p),
    udev_enumerate_ref=([udev_enumerate_p], udev_enumerate_p),
    udev_enumerate_unref=([udev_enumerate_p], None),
    udev_enumerate_add_match_subsystem=([udev_enumerate_p, c_char_p], c_int),
    udev_enumerate_add_nomatch_subsystem=([udev_enumerate_p, c_char_p], c_int),
    udev_enumerate_add_match_property=([udev_enumerate_p, c_char_p, c_char_p], c_int),
    udev_enumerate_add_match_sysattr=([udev_enumerate_p, c_char_p, c_char_p], c_int),
    udev_enumerate_add_nomatch_sysattr=([udev_enumerate_p, c_char_p, c_char_p], c_int),
    udev_enumerate_add_match_tag=([udev_enumerate_p, c_char_p], c_int),
    udev_enumerate_add_match_sysname=([udev_enumerate_p, c_char_p], c_int),
    udev_enumerate_add_match_parent=([udev_enumerate_p, udev_device_p], c_int),
    udev_enumerate_add_match_is_initialized=([udev_enumerate_p], c_int),
    udev_enumerate_scan_devices=([udev_enumerate_p], c_int),
    udev_enumerate_get_list_entry=([udev_enumerate_p], udev_list_entry_p),
    # list entries
    udev_list_entry_get_next=([udev_list_entry_p], udev_list_entry_p),
    udev_list_entry_get_name=([udev_list_entry_p], c_char_p),
    udev_list_entry_get_value=([udev_list_entry_p], c_char_p),
    # devices
    udev_device_ref=([udev_device_p], udev_device_p),
    udev_device_unref=([udev_device_p], None),
    udev_device_new_from_syspath=([udev_p, c_char_p], udev_device_p),
    udev_device_new_from_subsystem_sysname=(
        [udev_p, c_char_p, c_char_p],
        udev_device_p,
    ),
    udev_device_new_from_devnum=([udev_p, c_char, dev_t], udev_device_p),
    udev_device_new_from_device_id=([udev_p, c_char_p], udev_device_p),
    udev_device_new_from_environment=([udev_p], udev_device_p),
    udev_device_get_parent=([udev_device_p], udev_device_p),
    udev_device_get_parent_with_subsystem_devtype=(
        [udev_device_p, c_char_p, c_char_p],
        udev_device_p,
    ),
    udev_device_get_devpath=([udev_device_p], c_char_p),
    udev_device_get_subsystem=([udev_device_p], c_char_p),
    udev_device_get_syspath=([udev_device_p], c_char_p),
    udev_device_get_sysnum=([udev_device_p], c_char_p),
    udev_device_get_sysname=([udev_device_p], c_char_p),
    udev_device_get_driver=([udev_device_p], c_char_p),
    udev_device_get_devtype=([udev_device_p], c_char_p),
    udev_device_get_devnode=([udev_device_p], c_char_p),
    udev_device_get_property_value=([udev_device_p, c_char_p], c_char_p),
    udev_device_get_sysattr_value=([udev_device_p, c_char_p], c_char_p),
    udev_device_get_devnum=([udev_device_p], dev_t),
    udev_device_get_action=([udev_device_p], c_char_p),
    udev_device_get_seqnum=([udev_device_p], c_ulonglong),
    udev_device_get_is_initialized=([udev_device_p], c_int),
    udev_device_get_usec_since_initialized=([udev_device_p], c_ulonglong),
    udev_device_get_devlinks_list_entry=([udev_device_p], udev_list_entry_p),
    udev_device_get_tags_list_entry=([udev_device_p], udev_list_entry_p),
    udev_device_get_properties_list_entry=([udev_device_p], udev_list_entry_p),
    udev_device_get_sysattr_list_entry=([udev_device_p], udev_list_entry_p),
    udev_device_set_sysattr_value=([udev_device_p, c_char_p, c_char_p], c_int),
    udev_device_has_tag=([udev_device_p, c_char_p], c_int),
    # monitoring
    udev_monitor_ref=([udev_monitor_p], udev_monitor_p),
    udev_monitor_unref=([udev_monitor_p], None),
    udev_monitor_new_from_netlink=([udev_p, c_char_p], udev_monitor_p),
    udev_monitor_enable_receiving=([udev_monitor_p], c_int),
    udev_monitor_set_receive_buffer_size=([udev_monitor_p, c_int], c_int),
    udev_monitor_get_fd=([udev_monitor_p], c_int),
    udev_monitor_receive_device=([udev_monitor_p], udev_device_p),
    udev_monitor_filter_add_match_subsystem_devtype=(
        [udev_monitor_p, c_char_p, c_char_p],
        c_int,
    ),
    udev_monitor_filter_add_match_tag=([udev_monitor_p, c_char_p], c_int),
    udev_monitor_filter_update=([udev_monitor_p], c_int),
    udev_monitor_filter_remove=([udev_monitor_p], c_int),
    # hwdb
    udev_hwdb_ref=([udev_hwdb_p], udev_hwdb_p),
    udev_hwdb_unref=([udev_hwdb_p], None),
    udev_hwdb_new=([udev_p], udev_hwdb_p),
    udev_hwdb_get_properties_list_entry=(
        [udev_hwdb_p, c_char_p, c_uint],
        udev_list_entry_p,
    ),
)

ERROR_CHECKERS = dict(
    udev_device_get_action=None,
    udev_device_get_devlinks_list_entry=None,
    udev_device_get_devnode=None,
    udev_device_get_devnum=None,
    udev_device_get_devpath=None,
    udev_device_get_devtype=None,
    udev_device_get_driver=None,
    udev_device_get_is_initialized=None,
    udev_device_get_parent=None,
    udev_device_get_parent_with_subsystem_devtype=None,
    udev_device_get_properties_list_entry=None,
    udev_device_get_property_value=None,
    udev_device_get_seqnum=None,
    udev_device_get_subsystem=None,
    udev_device_get_sysattr_list_entry=None,
    udev_device_get_sysattr_value=None,
    udev_device_get_sysname=None,
    udev_device_get_sysnum=None,
    udev_device_get_syspath=None,
    udev_device_get_tags_list_entry=None,
    udev_device_get_usec_since_initialized=None,
    udev_device_has_tag=None,
    udev_device_new_from_device_id=None,
    udev_device_new_from_devnum=None,
    udev_device_new_from_environment=None,
    udev_device_new_from_subsystem_sysname=None,
    udev_device_new_from_syspath=None,
    udev_device_ref=None,
    udev_device_unref=None,
    udev_device_set_sysattr_value=check_negative_errorcode,
    udev_enumerate_add_match_parent=check_negative_errorcode,
    udev_enumerate_add_match_subsystem=check_negative_errorcode,
    udev_enumerate_add_nomatch_subsystem=check_negative_errorcode,
    udev_enumerate_add_match_property=check_negative_errorcode,
    udev_enumerate_add_match_sysattr=check_negative_errorcode,
    udev_enumerate_add_nomatch_sysattr=check_negative_errorcode,
    udev_enumerate_add_match_tag=check_negative_errorcode,
    udev_enumerate_add_match_sysname=check_negative_errorcode,
    udev_enumerate_add_match_is_initialized=check_negative_errorcode,
    udev_enumerate_get_list_entry=None,
    udev_enumerate_new=None,
    udev_enumerate_ref=None,
    udev_enumerate_scan_devices=None,
    udev_enumerate_unref=None,
    udev_get_dev_path=None,
    udev_get_log_priority=None,
    udev_get_run_path=None,
    udev_get_sys_path=None,
    udev_hwdb_get_properties_list_entry=None,
    udev_hwdb_new=None,
    udev_hwdb_ref=None,
    udev_hwdb_unref=None,
    udev_list_entry_get_name=None,
    udev_list_entry_get_next=None,
    udev_list_entry_get_value=None,
    udev_monitor_set_receive_buffer_size=check_errno_on_nonzero_return,
    # libudev doc says, enable_receiving returns a negative errno, but tests
    # show that this is not reliable, so query the real error code
    udev_monitor_enable_receiving=check_errno_on_nonzero_return,
    udev_monitor_receive_device=check_errno_on_null_pointer_return,
    udev_monitor_ref=None,
    udev_monitor_filter_add_match_subsystem_devtype=check_negative_errorcode,
    udev_monitor_filter_add_match_tag=check_negative_errorcode,
    udev_monitor_filter_update=check_errno_on_nonzero_return,
    udev_monitor_filter_remove=check_errno_on_nonzero_return,
    udev_monitor_get_fd=None,
    udev_monitor_new_from_netlink=None,
    udev_monitor_unref=None,
    udev_new=None,
    udev_ref=None,
    udev_set_log_priority=None,
    udev_unref=None,
)
07070100000027000081A40000000000000000000000016443203100000922000000000000000000000000000000000000002D00000000pyudev-0.24.1/src/pyudev/_ctypeslib/utils.py# -*- coding: utf-8 -*-
# Copyright (C) 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._ctypeslib.utils
    =======================

    Utilities for loading ctypeslib.

    .. moduleauthor::  Anne Mulhern  <amulhern@redhat.com>
"""

# isort: STDLIB
from ctypes import CDLL
from ctypes.util import find_library


def load_ctypes_library(name, signatures, error_checkers):
    """
    Load library ``name`` and return a :class:`ctypes.CDLL` object for it.

    :param str name: the library name
    :param signatures: signatures of methods
    :type signatures: dict of str * (tuple of (list of type) * type)
    :param error_checkers: error checkers for methods
    :type error_checkers: dict of str * ((int * ptr * arglist) -> int)

    The library has errno handling enabled.
    Important functions are given proper signatures and return types to support
    type checking and argument conversion.

    :returns: a loaded library
    :rtype: ctypes.CDLL
    :raises ImportError: if the library is not found
    """
    library_name = find_library(name)
    if not library_name:
        raise ImportError("No library named %s" % name)
    lib = CDLL(library_name, use_errno=True)
    # Add function signatures
    for funcname, signature in signatures.items():
        function = getattr(lib, funcname, None)
        if function:
            argtypes, restype = signature
            function.argtypes = argtypes
            function.restype = restype
            errorchecker = error_checkers.get(funcname)
            if errorchecker:
                function.errcheck = errorchecker
    return lib
07070100000028000081A400000000000000000000000164432031000013AA000000000000000000000000000000000000002400000000pyudev-0.24.1/src/pyudev/_errors.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.device._errors
    =====================

    Errors raised by Device methods.

    .. moduleauthor:: Sebastian Wiesner <lunaryorn@gmail.com>
"""

# isort: STDLIB
import abc


class DeviceError(Exception):
    """
    Any error raised when messing around w/ or trying to discover devices.
    """

    __metaclass__ = abc.ABCMeta


class DeviceNotFoundError(DeviceError):
    """
    An exception indicating that no :class:`Device` was found.

    .. versionchanged:: 0.5
       Rename from ``NoSuchDeviceError`` to its current name.
    """

    __metaclass__ = abc.ABCMeta


class DeviceNotFoundAtPathError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was
    found at a given path.
    """

    def __init__(self, sys_path):
        DeviceNotFoundError.__init__(self, sys_path)

    @property
    def sys_path(self):
        """
        The path that caused this error as string.
        """
        return self.args[0]

    def __str__(self):
        return "No device at {0!r}".format(self.sys_path)


class DeviceNotFoundByFileError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was
    found from the given filename.
    """


class DeviceNotFoundByInterfaceIndexError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found
    from the given interface index.
    """


class DeviceNotFoundByKernelDeviceError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was found
    from the given kernel device string.

    The format of the kernel device string is defined in the
    systemd.journal-fields man pages.
    """


class DeviceNotFoundByNameError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating that no :class:`Device` was
    found with a given name.
    """

    def __init__(self, subsystem, sys_name):
        DeviceNotFoundError.__init__(self, subsystem, sys_name)

    @property
    def subsystem(self):
        """
        The subsystem that caused this error as string.
        """
        return self.args[0]

    @property
    def sys_name(self):
        """
        The sys name that caused this error as string.
        """
        return self.args[1]

    def __str__(self):
        return "No device {0.sys_name!r} in {0.subsystem!r}".format(self)


class DeviceNotFoundByNumberError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` was found
    for a given device number.
    """

    def __init__(self, typ, number):
        DeviceNotFoundError.__init__(self, typ, number)

    @property
    def device_type(self):
        """
        The device type causing this error as string.  Either ``'char'`` or
        ``'block'``.
        """
        return self.args[0]

    @property
    def device_number(self):
        """
        The device number causing this error as integer.
        """
        return self.args[1]

    def __str__(self):
        return "No {0.device_type} device with number " "{0.device_number}".format(self)


class DeviceNotFoundInEnvironmentError(DeviceNotFoundError):
    """
    A :exc:`DeviceNotFoundError` indicating, that no :class:`Device` could
    be constructed from the process environment.
    """

    def __str__(self):
        return "No device found in environment"


class DeviceValueError(DeviceError):
    """
    Raised when a parameter has an unacceptable value.

    May also be raised when the parameter has an unacceptable type.
    """

    _FMT_STR = "value '%s' for parameter %s is unacceptable"

    def __init__(self, value, param, msg=None):
        """
        Initializer.

        :param object value: the value
        :param str param: the parameter
        :param str msg: an explanatory message
        """
        # pylint: disable=super-init-not-called
        self._value = value
        self._param = param
        self._msg = msg

    def __str__(self):
        if self._msg:
            fmt_str = self._FMT_STR + ": %s"
            return fmt_str % (self._value, self._param, self._msg)
        return self._FMT_STR % (self._value, self._param)
07070100000029000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001D00000000pyudev-0.24.1/src/pyudev/_os0707010000002A000081A400000000000000000000000164432031000003D3000000000000000000000000000000000000002900000000pyudev-0.24.1/src/pyudev/_os/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._os
    ==========

    Extras to compensate for deficiencies in python os module.

    .. moduleauthor::  mulhern  <amulhern@redhat.com>
"""

from . import pipe, poll
0707010000002B000081A4000000000000000000000001644320310000114C000000000000000000000000000000000000002500000000pyudev-0.24.1/src/pyudev/_os/pipe.py# -*- coding: utf-8 -*-
# Copyright (C) 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._os.pipe
    ===============

    Fallback implementations for pipe.

    1. pipe2 from python os module
    2. pipe2 from libc
    3. pipe from python os module

    The Pipe class wraps the chosen implementation.

    .. moduleauthor:: Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
import fcntl
import os
from functools import partial

# isort: LOCAL
from pyudev._ctypeslib.libc import ERROR_CHECKERS, FD_PAIR, SIGNATURES
from pyudev._ctypeslib.utils import load_ctypes_library

# Define O_CLOEXEC, if not present in os already
O_CLOEXEC = getattr(os, "O_CLOEXEC", 0o2000000)


def _pipe2_ctypes(libc, flags):
    """A ``pipe2`` implementation using ``pipe2`` from ctypes.

    ``libc`` is a :class:`ctypes.CDLL` object for libc.  ``flags`` is an
    integer providing the flags to ``pipe2``.

    Return a pair of file descriptors ``(r, w)``.

    """
    fds = FD_PAIR()
    libc.pipe2(fds, flags)
    return fds[0], fds[1]


def _pipe2_by_pipe(flags):
    """A ``pipe2`` implementation using :func:`os.pipe`.

    ``flags`` is an integer providing the flags to ``pipe2``.

    .. warning::

       This implementation is not atomic!

    Return a pair of file descriptors ``(r, w)``.

    """
    fds = os.pipe()
    if flags & os.O_NONBLOCK != 0:
        for file_descriptor in fds:
            set_fd_status_flag(file_descriptor, os.O_NONBLOCK)
    if flags & O_CLOEXEC != 0:
        for file_descriptor in fds:
            set_fd_flag(file_descriptor, O_CLOEXEC)
    return fds


def _get_pipe2_implementation():
    """
    Find the appropriate implementation for ``pipe2``.

    Return a function implementing ``pipe2``."""
    if hasattr(os, "pipe2"):
        return os.pipe2  # pylint: disable=no-member
    try:
        libc = load_ctypes_library("libc", SIGNATURES, ERROR_CHECKERS)
        return (
            partial(_pipe2_ctypes, libc) if hasattr(libc, "pipe2") else _pipe2_by_pipe
        )
    except ImportError:
        return _pipe2_by_pipe


_PIPE2 = _get_pipe2_implementation()


def set_fd_flag(fd, flag):  # pylint: disable=invalid-name
    """Set a flag on a file descriptor.

    ``fd`` is the file descriptor or file object, ``flag`` the flag as integer.

    """
    flags = fcntl.fcntl(fd, fcntl.F_GETFD, 0)
    fcntl.fcntl(fd, fcntl.F_SETFD, flags | flag)


def set_fd_status_flag(fd, flag):  # pylint: disable=invalid-name
    """Set a status flag on a file descriptor.

    ``fd`` is the file descriptor or file object, ``flag`` the flag as integer.

    """
    flags = fcntl.fcntl(fd, fcntl.F_GETFL, 0)
    fcntl.fcntl(fd, fcntl.F_SETFL, flags | flag)


class Pipe:
    """A unix pipe.

    A pipe object provides two file objects: :attr:`source` is a readable file
    object, and :attr:`sink` a writeable.  Bytes written to :attr:`sink` appear
    at :attr:`source`.

    Open a pipe with :meth:`open()`.

    """

    @classmethod
    def open(cls):
        """Open and return a new :class:`Pipe`.

        The pipe uses non-blocking IO."""
        source, sink = _PIPE2(os.O_NONBLOCK | O_CLOEXEC)
        return cls(source, sink)

    def __init__(self, source_fd, sink_fd):
        """Create a new pipe object from the given file descriptors.

        ``source_fd`` is a file descriptor for the readable side of the pipe,
        ``sink_fd`` is a file descriptor for the writeable side."""
        self.source = os.fdopen(source_fd, "rb", 0)
        self.sink = os.fdopen(sink_fd, "wb", 0)

    def close(self):
        """Closes both sides of the pipe."""
        try:
            self.source.close()
        finally:
            self.sink.close()
0707010000002C000081A40000000000000000000000016443203100000FB3000000000000000000000000000000000000002500000000pyudev-0.24.1/src/pyudev/_os/poll.py# -*- coding: utf-8 -*-
# Copyright (C) 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._os.poll
    ===============

    Operating system interface for pyudev.

    .. moduleauthor:: Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
import select

# isort: LOCAL
from pyudev._util import eintr_retry_call


class Poll:
    """A poll object.

    This object essentially provides a more convenient interface around
    :class:`select.poll`.

    """

    _EVENT_TO_MASK = {"r": select.POLLIN, "w": select.POLLOUT}

    @staticmethod
    def _has_event(events, event):
        return events & event != 0

    @classmethod
    def for_events(cls, *events):
        """Listen for ``events``.

        ``events`` is a list of ``(fd, event)`` pairs, where ``fd`` is a file
        descriptor or file object and ``event`` either ``'r'`` or ``'w'``.  If
        ``r``, listen for whether that is ready to be read.  If ``w``, listen
        for whether the channel is ready to be written to.

        """
        notifier = eintr_retry_call(select.poll)
        for fd, event in events:  # pylint: disable=invalid-name
            mask = cls._EVENT_TO_MASK.get(event)
            if not mask:
                raise ValueError(f"Unknown event type: {repr(event)}")
            notifier.register(fd, mask)
        return cls(notifier)

    def __init__(self, notifier):
        """Create a poll object for the given ``notifier``.

        ``notifier`` is the :class:`select.poll` object wrapped by the new poll
        object.

        """
        self._notifier = notifier

    def poll(self, timeout=None):
        """Poll for events.

        ``timeout`` is an integer specifying how long to wait for events (in
        milliseconds).  If omitted, ``None`` or negative, wait until an event
        occurs.

        Return a list of all events that occurred before ``timeout``, where
        each event is a pair ``(fd, event)``. ``fd`` is the integral file
        descriptor, and ``event`` a string indicating the event type.  If
        ``'r'``, there is data to read from ``fd``.  If ``'w'``, ``fd`` is
        writable without blocking now.  If ``'h'``, the file descriptor was
        hung up (i.e. the remote side of a pipe was closed).

        """
        # Return a list to allow clients to determine whether there are any
        # events at all with a simple truthiness test.
        return list(self._parse_events(eintr_retry_call(self._notifier.poll, timeout)))

    def _parse_events(self, events):
        """Parse ``events``.

        ``events`` is a list of events as returned by
        :meth:`select.poll.poll()`.

        Yield all parsed events.

        """
        for fd, event_mask in events:  # pylint: disable=invalid-name
            if self._has_event(event_mask, select.POLLNVAL):
                raise IOError(f"File descriptor not open: {repr(fd)}")
            if self._has_event(event_mask, select.POLLERR):
                raise IOError(f"Error while polling fd: {repr(fd)}")

            if self._has_event(event_mask, select.POLLIN):
                yield fd, "r"
            if self._has_event(event_mask, select.POLLOUT):
                yield fd, "w"
            if self._has_event(event_mask, select.POLLHUP):
                yield fd, "h"
0707010000002D000081A40000000000000000000000016443203100001B0A000000000000000000000000000000000000002500000000pyudev-0.24.1/src/pyudev/_qt_base.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._qt_base
    ===============

    Base mixin class for Qt4,Qt5 support.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: LOCAL
from pyudev.device import Device


class MonitorObserverMixin(object):
    """
    Base mixin for pyqt monitor observers.
    """

    # pylint: disable=too-few-public-methods

    def _setup_notifier(self, monitor, notifier_class):
        self.monitor = monitor
        self.notifier = notifier_class(monitor.fileno(), notifier_class.Read, self)
        self.notifier.activated[int].connect(self._process_udev_event)

    @property
    def enabled(self):
        """
        Whether this observer is enabled or not.

        If ``True`` (the default), this observer is enabled, and emits events.
        Otherwise it is disabled and does not emit any events.  This merely
        reflects the state of the ``enabled`` property of the underlying
        :attr:`notifier`.

        .. versionadded:: 0.14
        """
        return self.notifier.isEnabled()

    @enabled.setter
    def enabled(self, value):
        self.notifier.setEnabled(value)

    def _process_udev_event(self):
        """
        Attempt to receive a single device event from the monitor, process
        the event and emit corresponding signals.

        Called by ``QSocketNotifier``, if data is available on the udev
        monitoring socket.
        """
        device = self.monitor.poll(timeout=0)
        if device is not None:
            self._emit_event(device)

    def _emit_event(self, device):
        self.deviceEvent.emit(device)


class QUDevMonitorObserverMixin(MonitorObserverMixin):
    """
    Obsolete monitor observer mixin.
    """

    # pylint: disable=too-few-public-methods

    def _setup_notifier(self, monitor, notifier_class):
        MonitorObserverMixin._setup_notifier(self, monitor, notifier_class)
        self._action_signal_map = {
            "add": self.deviceAdded,
            "remove": self.deviceRemoved,
            "change": self.deviceChanged,
            "move": self.deviceMoved,
        }
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. " "Use pyudev.pyqt4.MonitorObserver instead.",
            DeprecationWarning,
        )

    def _emit_event(self, device):
        self.deviceEvent.emit(device.action, device)
        signal = self._action_signal_map.get(device.action)
        if signal is not None:
            signal.emit(device)


def make_init(qobject, socket_notifier):
    """
    Generates an initializer to observer the given ``monitor``
    (a :class:`~pyudev.Monitor`):

    ``parent`` is the parent :class:`~PyQt{4,5}.QtCore.QObject` of this
    object.  It is passed unchanged to the inherited constructor of
    :class:`~PyQt{4,5}.QtCore.QObject`.
    """

    def __init__(self, monitor, parent=None):
        qobject.__init__(self, parent)
        # pylint: disable=protected-access
        self._setup_notifier(monitor, socket_notifier)

    return __init__


class MonitorObserverGenerator(object):
    """
    Class to generate a MonitorObserver class.
    """

    # pylint: disable=too-few-public-methods

    @staticmethod
    def make_monitor_observer(qobject, signal, socket_notifier):
        """Generates an observer for device events integrating into the
        PyQt{4,5} mainloop.

        This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device
        events into Qt signals:

        >>> from pyudev import Context, Monitor
        >>> from pyudev.pyqt4 import MonitorObserver
        >>> context = Context()
        >>> monitor = Monitor.from_netlink(context)
        >>> monitor.filter_by(subsystem='input')
        >>> observer = MonitorObserver(monitor)
        >>> def device_event(device):
        ...     print('event {0} on device {1}'.format(device.action, device))
        >>> observer.deviceEvent.connect(device_event)
        >>> monitor.start()

        This class is a child of :class:`~{PySide, PyQt{4,5}}.QtCore.QObject`.

        """
        return type(
            str("MonitorObserver"),
            (qobject, MonitorObserverMixin),
            {
                str("__init__"): make_init(qobject, socket_notifier),
                str("deviceEvent"): signal(Device),
            },
        )


class QUDevMonitorObserverGenerator(object):
    """
    Class to generate a MonitorObserver class.
    """

    # pylint: disable=too-few-public-methods

    @staticmethod
    def make_monitor_observer(qobject, signal, socket_notifier):
        """Generates an observer for device events integrating into the
        PyQt{4,5} mainloop.

        This class inherits :class:`~PyQt{4,5}.QtCore.QObject` to turn device
        events into Qt signals:

        >>> from pyudev import Context, Monitor
        >>> from pyudev.pyqt4 import MonitorObserver
        >>> context = Context()
        >>> monitor = Monitor.from_netlink(context)
        >>> monitor.filter_by(subsystem='input')
        >>> observer = MonitorObserver(monitor)
        >>> def device_event(device):
        ...     print('event {0} on device {1}'.format(device.action, device))
        >>> observer.deviceEvent.connect(device_event)
        >>> monitor.start()

        This class is a child of :class:`~{PyQt{4,5}, PySide}.QtCore.QObject`.

        """
        return type(
            str("QUDevMonitorObserver"),
            (qobject, QUDevMonitorObserverMixin),
            {
                str("__init__"): make_init(qobject, socket_notifier),
                #: emitted upon arbitrary device events
                str("deviceEvent"): signal(str, Device),
                #: emitted if a device was added
                str("deviceAdded"): signal(Device),
                #: emitted if a device was removed
                str("deviceRemoved"): signal(Device),
                #: emitted if a device was changed
                str("deviceChanged"): signal(Device),
                #: emitted if a device was moved
                str("deviceMoved"): signal(Device),
            },
        )
0707010000002E000081A40000000000000000000000016443203100001A64000000000000000000000000000000000000002200000000pyudev-0.24.1/src/pyudev/_util.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev._util
    ============

    Internal utilities

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
import errno
import os
import stat
import sys
from subprocess import check_output


def ensure_byte_string(value):
    """
    Return the given ``value`` as bytestring.

    If the given ``value`` is not a byte string, but a real unicode string, it
    is encoded with the filesystem encoding (as in
    :func:`sys.getfilesystemencoding()`).
    """
    if not isinstance(value, bytes):
        value = value.encode(sys.getfilesystemencoding())
    return value


def ensure_unicode_string(value):
    """
    Return the given ``value`` as unicode string.

    If the given ``value`` is not a unicode string, but a byte string, it is
    decoded with the filesystem encoding (as in
    :func:`sys.getfilesystemencoding()`).
    """
    if not isinstance(value, str):
        value = value.decode(sys.getfilesystemencoding())
    return value


def property_value_to_bytes(value):
    """
    Return a byte string, which represents the given ``value`` in a way
    suitable as raw value of an udev property.

    If ``value`` is a boolean object, it is converted to ``'1'`` or ``'0'``,
    depending on whether ``value`` is ``True`` or ``False``.  If ``value`` is a
    byte string already, it is returned unchanged.  Anything else is simply
    converted to a unicode string, and then passed to
    :func:`ensure_byte_string`.
    """
    # udev represents boolean values as 1 or 0, therefore an explicit
    # conversion to int is required for boolean values
    if isinstance(value, bool):
        value = int(value)
    if isinstance(value, bytes):
        return value
    return ensure_byte_string(str(value))


def string_to_bool(value):
    """
    Convert the given unicode string ``value`` to a boolean object.

    If ``value`` is ``'1'``, ``True`` is returned.  If ``value`` is ``'0'``,
    ``False`` is returned.  Any other value raises a
    :exc:`~exceptions.ValueError`.
    """
    if value not in ("1", "0"):
        raise ValueError("Not a boolean value: {0!r}".format(value))
    return value == "1"


def udev_list_iterate(libudev, entry):
    """
    Iteration helper for udev list entry objects.

    Yield a tuple ``(name, value)``.  ``name`` and ``value`` are bytestrings
    containing the name and the value of the list entry.  The exact contents
    depend on the list iterated over.
    """
    while entry:
        name = libudev.udev_list_entry_get_name(entry)
        value = libudev.udev_list_entry_get_value(entry)
        yield (name, value)
        entry = libudev.udev_list_entry_get_next(entry)


def get_device_type(filename):
    """
    Get the device type of a device file.

    ``filename`` is a string containing the path of a device file.

    Return ``'char'`` if ``filename`` is a character device, or ``'block'`` if
    ``filename`` is a block device.  Raise :exc:`~exceptions.ValueError` if
    ``filename`` is no device file at all.  Raise
    :exc:`~exceptions.EnvironmentError` if ``filename`` does not exist or if
    its metadata was inaccessible.

    .. versionadded:: 0.15
    """
    mode = os.stat(filename).st_mode
    if stat.S_ISCHR(mode):
        return "char"
    elif stat.S_ISBLK(mode):
        return "block"
    else:
        raise ValueError("not a device file: {0!r}".format(filename))


def eintr_retry_call(func, *args, **kwargs):
    """
    Handle interruptions to an interruptible system call.

    Run an interruptible system call in a loop and retry if it raises EINTR.
    The signal calls that may raise EINTR prior to Python 3.5 are listed in
    PEP 0475.  Any calls to these functions must be wrapped in eintr_retry_call
    in order to handle EINTR returns in older versions of Python.

    This function is safe to use under Python 3.5 and newer since the wrapped
    function will simply return without raising EINTR.

    This function is based on _eintr_retry_call in python's subprocess.py.
    """

    # select.error inherits from Exception instead of OSError in Python 2
    # isort: STDLIB
    import select

    while True:
        try:
            return func(*args, **kwargs)
        except (OSError, IOError, select.error) as err:
            # If this is not an IOError or OSError, it's the old select.error
            # type, which means that the errno is only accessible via subscript
            if isinstance(err, (OSError, IOError)):
                error_code = err.errno
            else:
                error_code = err.args[0]

            if error_code == errno.EINTR:
                continue
            raise


def udev_version():
    """
    Get the version of the underlying udev library.

    udev doesn't use a standard major-minor versioning scheme, but instead
    labels releases with a single consecutive number.  Consequently, the
    version number returned by this function is a single integer, and not a
    tuple (like for instance the interpreter version in
    :data:`sys.version_info`).

    As libudev itself does not provide a function to query the version number,
    this function calls the ``udevadm`` utility, so be prepared to catch
    :exc:`~exceptions.EnvironmentError` and
    :exc:`~subprocess.CalledProcessError` if you call this function.

    Return the version number as single integer.  Raise
    :exc:`~exceptions.ValueError`, if the version number retrieved from udev
    could not be converted to an integer.  Raise
    :exc:`~exceptions.EnvironmentError`, if ``udevadm`` was not found, or could
    not be executed.  Raise :exc:`subprocess.CalledProcessError`, if
    ``udevadm`` returned a non-zero exit code.  On Python 2.7 or newer, the
    ``output`` attribute of this exception is correctly set.

    .. versionadded:: 0.8
    """
    output = ensure_unicode_string(check_output(["udevadm", "--version"]))
    return int(output.strip())
0707010000002F000081A400000000000000000000000164432031000033C2000000000000000000000000000000000000002100000000pyudev-0.24.1/src/pyudev/core.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.core
    ===========

    Core types and functions of :mod:`pyudev`.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: LOCAL
from pyudev._ctypeslib.libudev import ERROR_CHECKERS, SIGNATURES
from pyudev._ctypeslib.utils import load_ctypes_library
from pyudev._errors import DeviceNotFoundAtPathError
from pyudev._util import (
    ensure_byte_string,
    ensure_unicode_string,
    property_value_to_bytes,
    udev_list_iterate,
)
from pyudev.device import Devices


class Context(object):
    """
    A device database connection.

    This class represents a connection to the udev device database, and is
    really *the* central object to access udev.  You need an instance of this
    class for almost anything else in pyudev.

    This class itself gives access to various udev configuration data (e.g.
    :attr:`sys_path`, :attr:`device_path`), and provides device enumeration
    (:meth:`list_devices()`).

    Instances of this class can directly be given as ``udev *`` to functions
    wrapped through :mod:`ctypes`.
    """

    def __init__(self):
        """
        Create a new context.
        """
        self._libudev = load_ctypes_library("udev", SIGNATURES, ERROR_CHECKERS)
        self._as_parameter_ = self._libudev.udev_new()

    def __del__(self):
        if hasattr(self, "_libudev"):
            self._libudev.udev_unref(self)

    @property
    def sys_path(self):
        """
        The ``sysfs`` mount point defaulting to ``/sys'`` as unicode string.
        """
        if hasattr(self._libudev, "udev_get_sys_path"):
            return ensure_unicode_string(self._libudev.udev_get_sys_path(self))
        return "/sys"  # Fixed path since udev 183

    @property
    def device_path(self):
        """
        The device directory path defaulting to ``/dev`` as unicode string.
        """
        if hasattr(self._libudev, "udev_get_dev_path"):
            return ensure_unicode_string(self._libudev.udev_get_dev_path(self))
        return "/dev"  # Fixed path since udev 183

    @property
    def run_path(self):
        """
        The run runtime directory path defaulting to ``/run`` as unicode
        string.

        .. udevversion:: 167

        .. versionadded:: 0.10
        """
        if hasattr(self._libudev, "udev_get_run_path"):
            return ensure_unicode_string(self._libudev.udev_get_run_path(self))
        return "/run/udev"

    @property
    def log_priority(self):
        """
        The logging priority of the interal logging facitility of udev as
        integer with a standard :mod:`syslog` priority.  Assign to this
        property to change the logging priority.

        UDev uses the standard :mod:`syslog` priorities.  Constants for these
        priorities are defined in the :mod:`syslog` module in the standard
        library:

        >>> import syslog
        >>> context = pyudev.Context()
        >>> context.log_priority = syslog.LOG_DEBUG

        .. versionadded:: 0.9
        """
        return self._libudev.udev_get_log_priority(self)

    @log_priority.setter
    def log_priority(self, value):
        """
        Set the log priority.

        :param int value: the log priority.
        """
        self._libudev.udev_set_log_priority(self, value)

    def list_devices(self, **kwargs):
        """
        List all available devices.

        The arguments of this method are the same as for
        :meth:`Enumerator.match()`.  In fact, the arguments are simply passed
        straight to method :meth:`~Enumerator.match()`.

        This function creates and returns an :class:`Enumerator` object,
        that can be used to filter the list of devices, and eventually
        retrieve :class:`Device` objects representing matching devices.

        .. versionchanged:: 0.8
           Accept keyword arguments now for easy matching.
        """
        return Enumerator(self).match(**kwargs)


class Enumerator(object):
    """
    A filtered iterable of devices.

    To retrieve devices, simply iterate over an instance of this class.
    This operation yields :class:`Device` objects representing the available
    devices.

    Before iteration the device list can be filtered by subsystem or by
    property values using :meth:`match_subsystem` and
    :meth:`match_property`.  Multiple subsystem (property) filters are
    combined using a logical OR, filters of different types are combined
    using a logical AND.  The following filter for instance::

        devices.match_subsystem('block').match_property(
            'ID_TYPE', 'disk').match_property('DEVTYPE', 'disk')

    means the following::

        subsystem == 'block' and (ID_TYPE == 'disk' or DEVTYPE == 'disk')

    Once added, a filter cannot be removed anymore.  Create a new object
    instead.

    Instances of this class can directly be given as given ``udev_enumerate *``
    to functions wrapped through :mod:`ctypes`.
    """

    def __init__(self, context):
        """
        Create a new enumerator with the given ``context`` (a
        :class:`Context` instance).

        While you can create objects of this class directly, this is not
        recommended.  Call :method:`Context.list_devices()` instead.
        """
        if not isinstance(context, Context):
            raise TypeError("Invalid context object")
        self.context = context
        self._as_parameter_ = context._libudev.udev_enumerate_new(context)
        self._libudev = context._libudev

    def __del__(self):
        self._libudev.udev_enumerate_unref(self)

    def match(self, **kwargs):
        """
        Include devices according to the rules defined by the keyword
        arguments.  These keyword arguments are interpreted as follows:

        - The value for the keyword argument ``subsystem`` is forwarded to
          :meth:`match_subsystem()`.
        - The value for the keyword argument ``sys_name`` is forwared to
          :meth:`match_sys_name()`.
        - The value for the keyword argument ``tag`` is forwared to
          :meth:`match_tag()`.
        - The value for the keyword argument ``parent`` is forwared to
          :meth:`match_parent()`.
        - All other keyword arguments are forwareded one by one to
          :meth:`match_property()`.  The keyword argument itself is interpreted
          as property name, the value of the keyword argument as the property
          value.

        All keyword arguments are optional, calling this method without no
        arguments at all is simply a noop.

        Return the instance again.

        .. versionadded:: 0.8

        .. versionchanged:: 0.13
           Add ``parent`` keyword.
        """
        subsystem = kwargs.pop("subsystem", None)
        if subsystem is not None:
            self.match_subsystem(subsystem)
        sys_name = kwargs.pop("sys_name", None)
        if sys_name is not None:
            self.match_sys_name(sys_name)
        tag = kwargs.pop("tag", None)
        if tag is not None:
            self.match_tag(tag)
        parent = kwargs.pop("parent", None)
        if parent is not None:
            self.match_parent(parent)
        for prop, value in kwargs.items():
            self.match_property(prop, value)
        return self

    def match_subsystem(self, subsystem, nomatch=False):
        """
        Include all devices, which are part of the given ``subsystem``.

        ``subsystem`` is either a unicode string or a byte string, containing
        the name of the subsystem.  If ``nomatch`` is ``True`` (default is
        ``False``), the match is inverted:  A device is only included if it is
        *not* part of the given ``subsystem``.

        Note that, if a device has no subsystem, it is not included either
        with value of ``nomatch`` True or with value of ``nomatch`` False.

        Return the instance again.
        """
        match = (
            self._libudev.udev_enumerate_add_nomatch_subsystem
            if nomatch
            else self._libudev.udev_enumerate_add_match_subsystem
        )
        match(self, ensure_byte_string(subsystem))
        return self

    def match_sys_name(self, sys_name):
        """
        Include all devices with the given name.

        ``sys_name`` is a byte or unicode string containing the device name.

        Return the instance again.

        .. versionadded:: 0.8
        """
        self._libudev.udev_enumerate_add_match_sysname(
            self, ensure_byte_string(sys_name)
        )
        return self

    def match_property(self, prop, value):
        """
        Include all devices, whose ``prop`` has the given ``value``.

        ``prop`` is either a unicode string or a byte string, containing
        the name of the property to match.  ``value`` is a property value,
        being one of the following types:

        - :func:`int`
        - :func:`bool`
        - A byte string
        - Anything convertable to a unicode string (including a unicode string
          itself)

        Return the instance again.
        """
        self._libudev.udev_enumerate_add_match_property(
            self, ensure_byte_string(prop), property_value_to_bytes(value)
        )
        return self

    def match_attribute(self, attribute, value, nomatch=False):
        """
        Include all devices, whose ``attribute`` has the given ``value``.

        ``attribute`` is either a unicode string or a byte string, containing
        the name of a sys attribute to match.  ``value`` is an attribute value,
        being one of the following types:

        - :func:`int`,
        - :func:`bool`
        - A byte string
        - Anything convertable to a unicode string (including a unicode string
          itself)

        If ``nomatch`` is ``True`` (default is ``False``), the match is
        inverted:  A device is include if the ``attribute`` does *not* match
        the given ``value``.

        .. note::

           If ``nomatch`` is ``True``, devices which do not have the given
           ``attribute`` at all are also included.  In other words, with
           ``nomatch=True`` the given ``attribute`` is *not* guaranteed to
           exist on all returned devices.

        Return the instance again.
        """
        match = (
            self._libudev.udev_enumerate_add_match_sysattr
            if not nomatch
            else self._libudev.udev_enumerate_add_nomatch_sysattr
        )
        match(self, ensure_byte_string(attribute), property_value_to_bytes(value))
        return self

    def match_tag(self, tag):
        """
        Include all devices, which have the given ``tag`` attached.

        ``tag`` is a byte or unicode string containing the tag name.

        Return the instance again.

        .. udevversion:: 154

        .. versionadded:: 0.6
        """
        self._libudev.udev_enumerate_add_match_tag(self, ensure_byte_string(tag))
        return self

    def match_is_initialized(self):
        """
        Include only devices, which are initialized.

        Initialized devices have properly set device node permissions and
        context, and are (in case of network devices) fully renamed.

        Currently this will not affect devices which do not have device nodes
        and are not network interfaces.

        Return the instance again.

        .. seealso:: :attr:`Device.is_initialized`

        .. udevversion:: 165

        .. versionadded:: 0.8
        """
        self._libudev.udev_enumerate_add_match_is_initialized(self)
        return self

    def match_parent(self, parent):
        """
        Include all devices on the subtree of the given ``parent`` device.

        The ``parent`` device itself is also included.

        ``parent`` is a :class:`~pyudev.Device`.

        Return the instance again.

        .. udevversion:: 172

        .. versionadded:: 0.13
        """
        self._libudev.udev_enumerate_add_match_parent(self, parent)
        return self

    def __iter__(self):
        """
        Iterate over all matching devices.

        Yield :class:`Device` objects.
        """
        self._libudev.udev_enumerate_scan_devices(self)
        entry = self._libudev.udev_enumerate_get_list_entry(self)
        for name, _ in udev_list_iterate(self._libudev, entry):
            try:
                yield Devices.from_sys_path(self.context, name)
            except DeviceNotFoundAtPathError:
                continue
07070100000030000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000002000000000pyudev-0.24.1/src/pyudev/device07070100000031000081A400000000000000000000000164432031000003F4000000000000000000000000000000000000002C00000000pyudev-0.24.1/src/pyudev/device/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.device
    =============

    Device class implementation of :mod:`pyudev`.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

from ._device import Attributes, Device, Devices, Tags
07070100000032000081A4000000000000000000000001644320310000AE0D000000000000000000000000000000000000002B00000000pyudev-0.24.1/src/pyudev/device/_device.py# -*- coding: utf-8 -*-
# Copyright (C) 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.device._device
    =====================

    Device class implementation of :mod:`pyudev`.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
import collections
import os
import re
import sys
from datetime import timedelta

# isort: LOCAL
from pyudev._errors import (
    DeviceNotFoundAtPathError,
    DeviceNotFoundByFileError,
    DeviceNotFoundByInterfaceIndexError,
    DeviceNotFoundByKernelDeviceError,
    DeviceNotFoundByNameError,
    DeviceNotFoundByNumberError,
    DeviceNotFoundInEnvironmentError,
)
from pyudev._util import (
    ensure_byte_string,
    ensure_unicode_string,
    get_device_type,
    string_to_bool,
    udev_list_iterate,
)

# pylint: disable=too-many-lines


class Devices(object):
    """
    Class for constructing :class:`Device` objects from various kinds of data.
    """

    @classmethod
    def from_path(cls, context, path):
        """
        Create a device from a device ``path``.  The ``path`` may or may not
        start with the ``sysfs`` mount point:

        >>> from pyudev import Context, Device
        >>> context = Context()
        >>> Devices.from_path(context, '/devices/platform')
        Device(u'/sys/devices/platform')
        >>> Devices.from_path(context, '/sys/devices/platform')
        Device(u'/sys/devices/platform')

        ``context`` is the :class:`Context` in which to search the device.
        ``path`` is a device path as unicode or byte string.

        Return a :class:`Device` object for the device.  Raise
        :exc:`DeviceNotFoundAtPathError`, if no device was found for ``path``.

        .. versionadded:: 0.18
        """
        if not path.startswith(context.sys_path):
            path = os.path.join(context.sys_path, path.lstrip(os.sep))
        return cls.from_sys_path(context, path)

    @classmethod
    def from_sys_path(cls, context, sys_path):
        """
        Create a new device from a given ``sys_path``:

        >>> from pyudev import Context, Device
        >>> context = Context()
        >>> Devices.from_sys_path(context, '/sys/devices/platform')
        Device(u'/sys/devices/platform')

        ``context`` is the :class:`Context` in which to search the device.
        ``sys_path`` is a unicode or byte string containing the path of the
        device inside ``sysfs`` with the mount point included.

        Return a :class:`Device` object for the device.  Raise
        :exc:`DeviceNotFoundAtPathError`, if no device was found for
        ``sys_path``.

        .. versionadded:: 0.18
        """
        device = context._libudev.udev_device_new_from_syspath(
            context, ensure_byte_string(sys_path)
        )
        if not device:
            raise DeviceNotFoundAtPathError(sys_path)
        return Device(context, device)

    @classmethod
    def from_name(cls, context, subsystem, sys_name):
        """
        Create a new device from a given ``subsystem`` and a given
        ``sys_name``:

        >>> from pyudev import Context, Device
        >>> context = Context()
        >>> sda = Devices.from_name(context, 'block', 'sda')
        >>> sda
        Device(u'/sys/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda')
        >>> sda == Devices.from_path(context, '/block/sda')

        ``context`` is the :class:`Context` in which to search the device.
        ``subsystem`` and ``sys_name`` are byte or unicode strings, which
        denote the subsystem and the name of the device to create.

        Return a :class:`Device` object for the device.  Raise
        :exc:`DeviceNotFoundByNameError`, if no device was found with the given
        name.

        .. versionadded:: 0.18
        """
        sys_name = sys_name.replace("/", "!")
        device = context._libudev.udev_device_new_from_subsystem_sysname(
            context, ensure_byte_string(subsystem), ensure_byte_string(sys_name)
        )
        if not device:
            raise DeviceNotFoundByNameError(subsystem, sys_name)
        return Device(context, device)

    @classmethod
    def from_device_number(cls, context, typ, number):
        """
        Create a new device from a device ``number`` with the given device
        ``type``:

        >>> import os
        >>> from pyudev import Context, Device
        >>> ctx = Context()
        >>> major, minor = 8, 0
        >>> device = Devices.from_device_number(context, 'block',
        ...     os.makedev(major, minor))
        >>> device
        Device(u'/sys/devices/pci0000:00/0000:00:11.0/host0/target0:0:0/0:0:0:0/block/sda')
        >>> os.major(device.device_number), os.minor(device.device_number)
        (8, 0)

        Use :func:`os.makedev` to construct a device number from a major and a
        minor device number, as shown in the example above.

        .. warning::

           Device numbers are not unique across different device types.
           Passing a correct number with a wrong type may silently yield a
           wrong device object, so make sure to pass the correct device type.

        ``context`` is the :class:`Context`, in which to search the device.
        ``type`` is either ``'char'`` or ``'block'``, according to whether the
        device is a character or block device.  ``number`` is the device number
        as integer.

        Return a :class:`Device` object for the device with the given device
        ``number``.  Raise :exc:`DeviceNotFoundByNumberError`, if no device was
        found with the given device type and number.

        .. versionadded:: 0.18
        """
        device = context._libudev.udev_device_new_from_devnum(
            context, ensure_byte_string(typ[0]), number
        )
        if not device:
            raise DeviceNotFoundByNumberError(typ, number)
        return Device(context, device)

    @classmethod
    def from_device_file(cls, context, filename):
        """
        Create a new device from the given device file:

        >>> from pyudev import Context, Device
        >>> context = Context()
        >>> device = Devices.from_device_file(context, '/dev/sda')
        >>> device
        Device(u'/sys/devices/pci0000:00/0000:00:0d.0/host2/target2:0:0/2:0:0:0/block/sda')
        >>> device.device_node
        u'/dev/sda'

        .. warning::

           Though the example seems to suggest that ``device.device_node ==
           filename`` holds with ``device = Devices.from_device_file(context,
           filename)``, this is only true in a majority of cases.  There *can*
           be devices, for which this relation is actually false!  Thus, do
           *not* expect :attr:`~Device.device_node` to be equal to the given
           ``filename`` for the returned :class:`Device`.  Especially, use
           :attr:`~Device.device_node` if you need the device file of a
           :class:`Device` created with this method afterwards.

        ``context`` is the :class:`Context` in which to search the device.
        ``filename`` is a string containing the path of a device file.

        Return a :class:`Device` representing the given device file.  Raise
        :exc:`DeviceNotFoundByFileError` if ``filename`` is no device file
        at all or if ``filename`` does not exist or if its metadata was
        inaccessible.

        .. versionadded:: 0.18
        """
        try:
            device_type = get_device_type(filename)
            device_number = os.stat(filename).st_rdev
        except (EnvironmentError, ValueError) as err:
            raise DeviceNotFoundByFileError(err)

        return cls.from_device_number(context, device_type, device_number)

    @classmethod
    def from_interface_index(cls, context, ifindex):
        """
        Locate a device based on the interface index.

        :param `Context` context: the libudev context
        :param int ifindex: the interface index
        :returns: the device corresponding to the interface index
        :rtype: `Device`

        This method is only appropriate for network devices.
        """
        network_devices = context.list_devices(subsystem="net")
        dev = next(
            (d for d in network_devices if d.attributes.get("ifindex") == ifindex), None
        )
        if dev is not None:
            return dev
        else:
            raise DeviceNotFoundByInterfaceIndexError(ifindex)

    @classmethod
    def from_kernel_device(cls, context, kernel_device):
        """
        Locate a device based on the kernel device.

        :param `Context` context: the libudev context
        :param str kernel_device: the kernel device
        :returns: the device corresponding to ``kernel_device``
        :rtype: `Device`
        """
        switch_char = kernel_device[0]
        rest = kernel_device[1:]
        if switch_char in ("b", "c"):
            number_re = re.compile(r"^(?P<major>\d+):(?P<minor>\d+)$")
            match = number_re.match(rest)
            if match:
                number = os.makedev(
                    int(match.group("major")), int(match.group("minor"))
                )
                return cls.from_device_number(context, switch_char, number)
            else:
                raise DeviceNotFoundByKernelDeviceError(kernel_device)
        elif switch_char == "n":
            return cls.from_interface_index(context, rest)
        elif switch_char == "+":
            (subsystem, _, kernel_device_name) = rest.partition(":")
            if kernel_device_name and subsystem:
                return cls.from_name(context, subsystem, kernel_device_name)
            else:
                raise DeviceNotFoundByKernelDeviceError(kernel_device)
        else:
            raise DeviceNotFoundByKernelDeviceError(kernel_device)

    @classmethod
    def from_environment(cls, context):
        """
        Create a new device from the process environment (as in
        :data:`os.environ`).

        This only works reliable, if the current process is called from an
        udev rule, and is usually used for tools executed from ``IMPORT=``
        rules.  Use this method to create device objects in Python scripts
        called from udev rules.

        ``context`` is the library :class:`Context`.

        Return a :class:`Device` object constructed from the environment.
        Raise :exc:`DeviceNotFoundInEnvironmentError`, if no device could be
        created from the environment.

        .. udevversion:: 152

        .. versionadded:: 0.18
        """
        device = context._libudev.udev_device_new_from_environment(context)
        if not device:
            raise DeviceNotFoundInEnvironmentError()
        return Device(context, device)

    @classmethod
    def METHODS(cls):  # pylint: disable=invalid-name
        """
        Return methods that obtain a :class:`Device` from a variety of
        different data.

        :return: a list of from_* methods.
        :rtype: list of class methods

        .. versionadded:: 0.18
        """
        return [  # pragma: no cover
            cls.from_device_file,
            cls.from_device_number,
            cls.from_name,
            cls.from_path,
            cls.from_sys_path,
        ]


class Device(collections.abc.Mapping):
    # pylint: disable=too-many-public-methods
    """
    A single device with attached attributes and properties.

    A device also has a set of udev-specific attributes like the path
    inside ``sysfs``.

    :class:`Device` objects compare equal and unequal to other devices and
    to strings (based on :attr:`device_path`).  However, there is no
    ordering on :class:`Device` objects, and the corresponding operators
    ``>``, ``<``, ``<=`` and ``>=`` raise :exc:`~exceptions.TypeError`.

    .. warning::

       Currently, Device extends Mapping. The mapping that it stores is that
       of udev property names to udev property values. This use is deprecated
       and Device will no longer extend Mapping in 1.0. To look up udev
       properties, use the Device.properties property.

    .. warning::

       **Never** use object identity (``is`` operator) to compare
       :class:`Device` objects.  :mod:`pyudev` may create multiple
       :class:`Device` objects for the same device.  Instead compare
       devices by value using ``==`` or ``!=``.

    :class:`Device` objects are hashable and can therefore be used as keys
    in dictionaries and sets.

    They can also be given directly as ``udev_device *`` to functions wrapped
    through :mod:`ctypes`.
    """

    @classmethod
    def from_path(cls, context, path):  # pragma: no cover
        """
        .. versionadded:: 0.4
        .. deprecated:: 0.18
           Use :class:`Devices.from_path` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use equivalent Devices method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return Devices.from_path(context, path)

    @classmethod
    def from_sys_path(cls, context, sys_path):  # pragma: no cover
        """
        .. versionchanged:: 0.4
           Raise :exc:`NoSuchDeviceError` instead of returning ``None``, if
           no device was found for ``sys_path``.
        .. versionchanged:: 0.5
           Raise :exc:`DeviceNotFoundAtPathError` instead of
           :exc:`NoSuchDeviceError`.
        .. deprecated:: 0.18
           Use :class:`Devices.from_sys_path` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use equivalent Devices method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return Devices.from_sys_path(context, sys_path)

    @classmethod
    def from_name(cls, context, subsystem, sys_name):  # pragma: no cover
        """
        .. versionadded:: 0.5
        .. deprecated:: 0.18
           Use :class:`Devices.from_name` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use equivalent Devices method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return Devices.from_name(context, subsystem, sys_name)

    @classmethod
    def from_device_number(cls, context, typ, number):  # pragma: no cover
        """
        .. versionadded:: 0.11
        .. deprecated:: 0.18
           Use :class:`Devices.from_device_number` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use equivalent Devices method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return Devices.from_device_number(context, typ, number)

    @classmethod
    def from_device_file(cls, context, filename):  # pragma: no cover
        """
        .. versionadded:: 0.15
        .. deprecated:: 0.18
           Use :class:`Devices.from_device_file` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use equivalent Devices method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return Devices.from_device_file(context, filename)

    @classmethod
    def from_environment(cls, context):  # pragma: no cover
        """
        .. versionadded:: 0.6
        .. deprecated:: 0.18
           Use :class:`Devices.from_environment` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use equivalent Devices method instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return Devices.from_environment(context)

    def __init__(self, context, _device):
        collections.abc.Mapping.__init__(self)
        self.context = context
        self._as_parameter_ = _device
        self._libudev = context._libudev

    def __del__(self):
        self._libudev.udev_device_unref(self)

    def __repr__(self):
        return "Device({0.sys_path!r})".format(self)

    @property
    def parent(self):
        """
        The parent :class:`Device` or ``None``, if there is no parent
        device.
        """
        parent = self._libudev.udev_device_get_parent(self)
        if not parent:
            return None
        # the parent device is not referenced, thus forcibly acquire a
        # reference
        return Device(self.context, self._libudev.udev_device_ref(parent))

    @property
    def children(self):
        """
        Yield all direct children of this device.

        .. note::

           In udev, parent-child relationships are generally ambiguous, i.e.
           a parent can have multiple children, *and* a child can have multiple
           parents. Hence, `child.parent == parent` does generally *not* hold
           for all `child` objects in `parent.children`. In other words,
           the :attr:`parent` of a device in this property can be different
           from this device!

        .. note::

           As the underlying library does not provide any means to directly
           query the children of a device, this property performs a linear
           search through all devices.

        Return an iterable yielding a :class:`Device` object for each direct
        child of this device.

        .. udevversion:: 172

        .. versionchanged:: 0.13
           Requires udev version 172 now.
        """
        for device in self.context.list_devices().match_parent(self):
            if device != self:
                yield device

    @property
    def ancestors(self):
        """
        Yield all ancestors of this device from bottom to top.

        Return an iterator yielding a :class:`Device` object for each
        ancestor of this device from bottom to top.

        .. versionadded:: 0.16
        """
        parent = self.parent
        while parent is not None:
            yield parent
            parent = parent.parent

    def find_parent(self, subsystem, device_type=None):
        """
        Find the parent device with the given ``subsystem`` and
        ``device_type``.

        ``subsystem`` is a byte or unicode string containing the name of the
        subsystem, in which to search for the parent.  ``device_type`` is a
        byte or unicode string holding the expected device type of the parent.
        It can be ``None`` (the default), which means, that no specific device
        type is expected.

        Return a parent :class:`Device` within the given ``subsystem`` and, if
        ``device_type`` is not ``None``, with the given ``device_type``, or
        ``None``, if this device has no parent device matching these
        constraints.

        .. versionadded:: 0.9
        """
        subsystem = ensure_byte_string(subsystem)
        if device_type is not None:
            device_type = ensure_byte_string(device_type)
        parent = self._libudev.udev_device_get_parent_with_subsystem_devtype(
            self, subsystem, device_type
        )
        if not parent:
            return None
        # parent device is not referenced, thus forcibly acquire a reference
        return Device(self.context, self._libudev.udev_device_ref(parent))

    def traverse(self):
        """
        Traverse all parent devices of this device from bottom to top.

        Return an iterable yielding all parent devices as :class:`Device`
        objects, *not* including the current device.  The last yielded
        :class:`Device` is the top of the device hierarchy.

        .. deprecated:: 0.16
           Will be removed in 1.0. Use :attr:`ancestors` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use Device.ancestors instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.ancestors

    @property
    def sys_path(self):
        """
        Absolute path of this device in ``sysfs`` including the ``sysfs``
        mount point as unicode string.
        """
        return ensure_unicode_string(self._libudev.udev_device_get_syspath(self))

    @property
    def device_path(self):
        """
        Kernel device path as unicode string.  This path uniquely identifies
        a single device.

        Unlike :attr:`sys_path`, this path does not contain the ``sysfs``
        mount point.  However, the path is absolute and starts with a slash
        ``'/'``.
        """
        return ensure_unicode_string(self._libudev.udev_device_get_devpath(self))

    @property
    def subsystem(self):
        """
        Name of the subsystem this device is part of as unicode string.

        :returns: name of subsystem if found, else None
        :rtype: unicode string or NoneType
        """
        subsys = self._libudev.udev_device_get_subsystem(self)
        return None if subsys is None else ensure_unicode_string(subsys)

    @property
    def sys_name(self):
        """
        Device file name inside ``sysfs`` as unicode string.
        """
        return ensure_unicode_string(self._libudev.udev_device_get_sysname(self))

    @property
    def sys_number(self):
        """
        The trailing number of the :attr:`sys_name` as unicode string, or
        ``None``, if the device has no trailing number in its name.

        .. note::

           The number is returned as unicode string to preserve the exact
           format of the number, especially any leading zeros:

           >>> from pyudev import Context, Device
           >>> context = Context()
           >>> device = Devices.from_path(context, '/sys/devices/LNXSYSTM:00')
           >>> device.sys_number
           u'00'

           To work with numbers, explicitly convert them to ints:

           >>> int(device.sys_number)
           0

        .. versionadded:: 0.11
        """
        number = self._libudev.udev_device_get_sysnum(self)
        return ensure_unicode_string(number) if number is not None else None

    @property
    def device_type(self):
        """
        Device type as unicode string, or ``None``, if the device type is
        unknown.

        >>> from pyudev import Context
        >>> context = Context()
        >>> for device in context.list_devices(subsystem='net'):
        ...     '{0} - {1}'.format(device.sys_name, device.device_type or 'ethernet')
        ...
        u'eth0 - ethernet'
        u'wlan0 - wlan'
        u'lo - ethernet'
        u'vboxnet0 - ethernet'

        .. versionadded:: 0.10
        """
        device_type = self._libudev.udev_device_get_devtype(self)
        if device_type is None:
            return None
        return ensure_unicode_string(device_type)

    @property
    def driver(self):
        """
        The driver name as unicode string, or ``None``, if there is no
        driver for this device.

        .. versionadded:: 0.5
        """
        driver = self._libudev.udev_device_get_driver(self)
        return ensure_unicode_string(driver) if driver is not None else None

    @property
    def device_node(self):
        """
        Absolute path to the device node of this device as unicode string or
        ``None``, if this device doesn't have a device node.  The path
        includes the device directory (see :attr:`Context.device_path`).

        This path always points to the actual device node associated with
        this device, and never to any symbolic links to this device node.
        See :attr:`device_links` to get a list of symbolic links to this
        device node.

        .. warning::

           For devices created with :meth:`from_device_file()`, the value of
           this property is not necessary equal to the ``filename`` given to
           :meth:`from_device_file()`.
        """
        node = self._libudev.udev_device_get_devnode(self)
        return ensure_unicode_string(node) if node is not None else None

    @property
    def device_number(self):
        """
        The device number of the associated device as integer, or ``0``, if no
        device number is associated.

        Use :func:`os.major` and :func:`os.minor` to decompose the device
        number into its major and minor number:

        >>> import os
        >>> from pyudev import Context, Device
        >>> context = Context()
        >>> sda = Devices.from_name(context, 'block', 'sda')
        >>> sda.device_number
        2048L
        >>> (os.major(sda.device_number), os.minor(sda.device_number))
        (8, 0)

        For devices with an associated :attr:`device_node`, this is the same as
        the ``st_rdev`` field of the stat result of the :attr:`device_node`:

        >>> os.stat(sda.device_node).st_rdev
        2048

        .. versionadded:: 0.11
        """
        return self._libudev.udev_device_get_devnum(self)

    @property
    def is_initialized(self):
        """
        ``True``, if the device is initialized, ``False`` otherwise.

        A device is initialized, if udev has already handled this device and
        has set up device node permissions and context, or renamed a network
        device.

        Consequently, this property is only implemented for devices with a
        device node or for network devices.  On all other devices this property
        is always ``True``.

        It is *not* recommended, that you use uninitialized devices.

        .. seealso:: :attr:`time_since_initialized`

        .. udevversion:: 165

        .. versionadded:: 0.8
        """
        return bool(self._libudev.udev_device_get_is_initialized(self))

    @property
    def time_since_initialized(self):
        """
        The time elapsed since initialization as :class:`~datetime.timedelta`.

        This property is only implemented on devices, which need to store
        properties in the udev database.  On all other devices this property is
        simply zero :class:`~datetime.timedelta`.

        .. seealso:: :attr:`is_initialized`

        .. udevversion:: 165

        .. versionadded:: 0.8
        """
        microseconds = self._libudev.udev_device_get_usec_since_initialized(self)
        return timedelta(microseconds=microseconds)

    @property
    def device_links(self):
        """
        An iterator, which yields the absolute paths (including the device
        directory, see :attr:`Context.device_path`) of all symbolic links
        pointing to the :attr:`device_node` of this device.  The paths are
        unicode strings.

        UDev can create symlinks to the original device node (see
        :attr:`device_node`) inside the device directory.  This is often
        used to assign a constant, fixed device node to devices like
        removeable media, which technically do not have a constant device
        node, or to map a single device into multiple device hierarchies.
        The property provides access to all such symbolic links, which were
        created by UDev for this device.

        .. warning::

           Links are not necessarily resolved by
           :meth:`Devices.from_device_file()`. Hence do *not* rely on
           ``Devices.from_device_file(context, link).device_path ==
           device.device_path`` from any ``link`` in ``device.device_links``.
        """
        devlinks = self._libudev.udev_device_get_devlinks_list_entry(self)
        for name, _ in udev_list_iterate(self._libudev, devlinks):
            yield ensure_unicode_string(name)

    @property
    def action(self):
        """
        The device event action as string, or ``None``, if this device was not
        received from a :class:`Monitor`.

        Usual actions are:

        ``'add'``
          A device has been added (e.g. a USB device was plugged in)
        ``'remove'``
          A device has been removed (e.g. a USB device was unplugged)
        ``'change'``
          Something about the device changed (e.g. a device property)
        ``'online'``
          The device is online now
        ``'offline'``
          The device is offline now

        .. warning::

           Though the actions listed above are the most common, this property
           *may* return other values, too, so be prepared to handle unknown
           actions!

        .. versionadded:: 0.16
        """
        action = self._libudev.udev_device_get_action(self)
        return ensure_unicode_string(action) if action is not None else None

    @property
    def sequence_number(self):
        """
        The device event sequence number as integer, or ``0`` if this device
        has no sequence number, i.e. was not received from a :class:`Monitor`.

        .. versionadded:: 0.16
        """
        return self._libudev.udev_device_get_seqnum(self)

    @property
    def attributes(self):
        """
        The system attributes of this device as read-only
        :class:`Attributes` mapping.

        System attributes are basically normal files inside the device
        directory.  These files contain all sorts of information about the
        device, which may not be reflected by properties.  These attributes
        are commonly used for matching in udev rules, and can be printed
        using ``udevadm info --attribute-walk``.

        The values of these attributes are not always proper strings, and
        can contain arbitrary bytes.

        :returns: an Attributes object, useful for reading attributes
        :rtype: Attributes

        .. versionadded:: 0.5
        """
        # do *not* cache the created object in an attribute of this class.
        # Doing so creates an uncollectable reference cycle between Device and
        # Attributes, because Attributes refers to this object through
        # Attributes.device.
        return Attributes(self)

    @property
    def properties(self):
        """
        The udev properties of this object as read-only Properties mapping.

        .. versionadded:: 0.21
        """
        return Properties(self)

    @property
    def tags(self):
        """
        A :class:`Tags` object representing the tags attached to this device.

        The :class:`Tags` object supports a test for a single tag as well as
        iteration over all tags:

        >>> from pyudev import Context
        >>> context = Context()
        >>> device = next(iter(context.list_devices(tag='systemd')))
        >>> 'systemd' in device.tags
        True
        >>> list(device.tags)
        [u'seat', u'systemd', u'uaccess']

        Tags are arbitrary classifiers that can be attached to devices by udev
        scripts and daemons.  For instance, systemd_ uses tags for multi-seat_
        support.

        .. _systemd: http://freedesktop.org/wiki/Software/systemd
        .. _multi-seat: http://www.freedesktop.org/wiki/Software/systemd/multiseat

        .. udevversion:: 154

        .. versionadded:: 0.6

        .. versionchanged:: 0.13
           Return a :class:`Tags` object now.
        """
        return Tags(self)

    def __iter__(self):
        """
        Iterate over the names of all properties defined for this device.

        Return a generator yielding the names of all properties of this
        device as unicode strings.

        .. deprecated:: 0.21
           Will be removed in 1.0. Access properties with Device.properties.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Access properties with Device.properties.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.properties.__iter__()

    def __len__(self):
        """
        Return the amount of properties defined for this device as integer.

        .. deprecated:: 0.21
           Will be removed in 1.0. Access properties with Device.properties.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Access properties with Device.properties.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.properties.__len__()

    def __getitem__(self, prop):
        """
        Get the given property from this device.

        ``prop`` is a unicode or byte string containing the name of the
        property.

        Return the property value as unicode string, or raise a
        :exc:`~exceptions.KeyError`, if the given property is not defined
        for this device.

        .. deprecated:: 0.21
           Will be removed in 1.0. Access properties with Device.properties.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Access properties with Device.properties.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.properties.__getitem__(prop)

    def asint(self, prop):
        """
        Get the given property from this device as integer.

        ``prop`` is a unicode or byte string containing the name of the
        property.

        Return the property value as integer. Raise a
        :exc:`~exceptions.KeyError`, if the given property is not defined
        for this device, or a :exc:`~exceptions.ValueError`, if the property
        value cannot be converted to an integer.

        .. deprecated:: 0.21
           Will be removed in 1.0. Use Device.properties.asint() instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use Device.properties.asint instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.properties.asint(prop)

    def asbool(self, prop):
        """
        Get the given property from this device as boolean.

        A boolean property has either a value of ``'1'`` or of ``'0'``,
        where ``'1'`` stands for ``True``, and ``'0'`` for ``False``.  Any
        other value causes a :exc:`~exceptions.ValueError` to be raised.

        ``prop`` is a unicode or byte string containing the name of the
        property.

        Return ``True``, if the property value is ``'1'`` and ``False``, if
        the property value is ``'0'``.  Any other value raises a
        :exc:`~exceptions.ValueError`.  Raise a :exc:`~exceptions.KeyError`,
        if the given property is not defined for this device.

        .. deprecated:: 0.21
           Will be removed in 1.0. Use Device.properties.asbool() instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use Device.properties.asbool instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        return self.properties.asbool(prop)

    def __hash__(self):
        return hash(self.device_path)

    def __eq__(self, other):
        if isinstance(other, Device):
            return self.device_path == other.device_path
        return self.device_path == other

    def __ne__(self, other):
        if isinstance(other, Device):
            return self.device_path != other.device_path
        return self.device_path != other

    def __gt__(self, other):
        raise TypeError("Device not orderable")

    def __lt__(self, other):
        raise TypeError("Device not orderable")

    def __le__(self, other):
        raise TypeError("Device not orderable")

    def __ge__(self, other):
        raise TypeError("Device not orderable")


class Properties(collections.abc.Mapping):
    """
    udev properties :class:`Device` objects.

    .. versionadded:: 0.21
    """

    def __init__(self, device):
        collections.abc.Mapping.__init__(self)
        self.device = device
        self._libudev = device._libudev

    def __iter__(self):
        """
        Iterate over the names of all properties defined for the device.

        Return a generator yielding the names of all properties of this
        device as unicode strings.
        """
        properties = self._libudev.udev_device_get_properties_list_entry(self.device)
        for name, _ in udev_list_iterate(self._libudev, properties):
            yield ensure_unicode_string(name)

    def __len__(self):
        """
        Return the amount of properties defined for this device as integer.
        """
        properties = self._libudev.udev_device_get_properties_list_entry(self.device)
        return sum(1 for _ in udev_list_iterate(self._libudev, properties))

    def __getitem__(self, prop):
        """
        Get the given property from this device.

        ``prop`` is a unicode or byte string containing the name of the
        property.

        Return the property value as unicode string, or raise a
        :exc:`~exceptions.KeyError`, if the given property is not defined
        for this device.
        """
        value = self._libudev.udev_device_get_property_value(
            self.device, ensure_byte_string(prop)
        )
        if value is None:
            raise KeyError(prop)
        return ensure_unicode_string(value)

    def asint(self, prop):
        """
        Get the given property from this device as integer.

        ``prop`` is a unicode or byte string containing the name of the
        property.

        Return the property value as integer. Raise a
        :exc:`~exceptions.KeyError`, if the given property is not defined
        for this device, or a :exc:`~exceptions.ValueError`, if the property
        value cannot be converted to an integer.
        """
        return int(self[prop])

    def asbool(self, prop):
        """
        Get the given property from this device as boolean.

        A boolean property has either a value of ``'1'`` or of ``'0'``,
        where ``'1'`` stands for ``True``, and ``'0'`` for ``False``.  Any
        other value causes a :exc:`~exceptions.ValueError` to be raised.

        ``prop`` is a unicode or byte string containing the name of the
        property.

        Return ``True``, if the property value is ``'1'`` and ``False``, if
        the property value is ``'0'``.  Any other value raises a
        :exc:`~exceptions.ValueError`.  Raise a :exc:`~exceptions.KeyError`,
        if the given property is not defined for this device.
        """
        return string_to_bool(self[prop])


class Attributes(object):
    """
    udev attributes for :class:`Device` objects.

    .. versionadded:: 0.5
    """

    def __init__(self, device):
        self.device = device
        self._libudev = device._libudev

    @property
    def available_attributes(self):
        """
        Yield the ``available`` attributes for the device.

        It is not guaranteed that a key in this list will have a value.
        It is not guaranteed that a key not in this list will not have a value.

        It is guaranteed that the keys in this list are the keys that libudev
        considers to be "available" attributes.

        If libudev version does not define udev_device_get_sysattr_list_entry()
        yields nothing.

        See rhbz#1267584.
        """
        if not hasattr(self._libudev, "udev_device_get_sysattr_list_entry"):
            return  # pragma: no cover
        attrs = self._libudev.udev_device_get_sysattr_list_entry(self.device)
        for attribute, _ in udev_list_iterate(self._libudev, attrs):
            yield ensure_unicode_string(attribute)

    def _get(self, attribute):
        """
        Get the given system ``attribute`` for the device.

        :param attribute: the key for an attribute value
        :type attribute: unicode or byte string
        :returns: the value corresponding to ``attribute``
        :rtype: an arbitrary sequence of bytes
        :raises KeyError: if no value found
        """
        value = self._libudev.udev_device_get_sysattr_value(
            self.device, ensure_byte_string(attribute)
        )
        if value is None:
            raise KeyError(attribute)
        return value

    def get(self, attribute, default=None):
        """
        Get the given system ``attribute`` for the device.

        :param attribute: the key for an attribute value
        :type attribute: unicode or byte string
        :param default: a default if no corresponding value found
        :type default: a sequence of bytes
        :returns: the value corresponding to ``attribute`` or ``default``
        :rtype: object
        """
        try:
            return self._get(attribute)
        except KeyError:
            return default

    def asstring(self, attribute):
        """
        Get the given ``attribute`` for the device as unicode string.

        :param attribute: the key for an attribute value
        :type attribute: unicode or byte string
        :returns: the value corresponding to ``attribute``, as unicode
        :rtype: unicode
        :raises KeyError: if no value found for ``attribute``
        :raises UnicodeDecodeError: if value is not convertible
        """
        return ensure_unicode_string(self._get(attribute))

    def asint(self, attribute):
        """
        Get the given ``attribute`` as an int.

        :param attribute: the key for an attribute value
        :type attribute: unicode or byte string
        :returns: the value corresponding to ``attribute``, as an int
        :rtype: int
        :raises KeyError: if no value found for ``attribute``
        :raises UnicodeDecodeError: if value is not convertible to unicode
        :raises ValueError: if unicode value can not be converted to an int
        """
        return int(self.asstring(attribute))

    def asbool(self, attribute):
        """
        Get the given ``attribute`` from this device as a bool.

        :param attribute: the key for an attribute value
        :type attribute: unicode or byte string
        :returns: the value corresponding to ``attribute``, as bool
        :rtype: bool
        :raises KeyError: if no value found for ``attribute``
        :raises UnicodeDecodeError: if value is not convertible to unicode
        :raises ValueError: if unicode value can not be converted to a bool

        A boolean attribute has either a value of ``'1'`` or of ``'0'``,
        where ``'1'`` stands for ``True``, and ``'0'`` for ``False``.  Any
        other value causes a :exc:`~exceptions.ValueError` to be raised.
        """
        return string_to_bool(self.asstring(attribute))


class Tags(collections.abc.Iterable, collections.abc.Container):
    """
    A iterable over :class:`Device` tags.

    Subclasses the ``Container`` and the ``Iterable`` ABC.
    """

    # pylint: disable=too-few-public-methods

    def __init__(self, device):
        # pylint: disable=super-init-not-called
        collections.abc.Iterable.__init__(self)
        self.device = device
        self._libudev = device._libudev

    def _has_tag(self, tag):
        """
        Whether ``tag`` exists.

        :param tag: unicode string with name of tag
        :rtype: bool
        """
        if hasattr(self._libudev, "udev_device_has_tag"):
            return bool(
                self._libudev.udev_device_has_tag(self.device, ensure_byte_string(tag))
            )
        return any(t == tag for t in self)  # pragma: no cover

    def __contains__(self, tag):
        """
        Check for existence of ``tag``.

        ``tag`` is a tag as unicode string.

        Return ``True``, if ``tag`` is attached to the device, ``False``
        otherwise.
        """
        return self._has_tag(tag)

    def __iter__(self):
        """
        Iterate over all tags.

        Yield each tag as unicode string.
        """
        tags = self._libudev.udev_device_get_tags_list_entry(self.device)
        for tag, _ in udev_list_iterate(self._libudev, tags):
            yield ensure_unicode_string(tag)
07070100000033000081A40000000000000000000000016443203100002C4F000000000000000000000000000000000000002500000000pyudev-0.24.1/src/pyudev/discover.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.discover
    ===============

    Tools to discover a device given limited information.

    .. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import abc
import functools
import os
import re

# isort: LOCAL
from pyudev._errors import DeviceNotFoundError
from pyudev.device import Devices


def wrap_exception(func):
    """
    Allow Device discovery methods to return None instead of raising an
    exception.
    """

    @functools.wraps(func)
    def the_func(*args, **kwargs):
        """
        Returns result of calling ``func`` on ``args``, ``kwargs``.
        Returns None if ``func`` raises :exc:`DeviceNotFoundError`.
        """
        try:
            return func(*args, **kwargs)
        except DeviceNotFoundError:
            return None

    return the_func


class Hypothesis(object):
    """
    Represents a hypothesis about the meaning of the device identifier.
    """

    __metaclass__ = abc.ABCMeta

    @classmethod
    @abc.abstractmethod
    def match(cls, value):  # pragma: no cover
        """
        Match the given string according to the hypothesis.

        The purpose of this method is to obtain a value corresponding to
        ``value`` if that is possible. It may use a regular expression, but
        in general it should just return ``value`` and let the lookup method
        sort out the rest.

        :param str value: the string to inspect
        :returns: the matched thing or None if unmatched
        :rtype: the type of lookup's key parameter or NoneType
        """
        raise NotImplementedError()

    @classmethod
    @abc.abstractmethod
    def lookup(cls, context, key):  # pragma: no cover
        """
        Lookup the given string according to the hypothesis.

        :param Context context: the pyudev context
        :param key: a key with which to lookup the device
        :type key: the type of match's return value if not None
        :returns: a list of Devices obtained
        :rtype: frozenset of :class:`Device`
        """
        raise NotImplementedError()

    @classmethod
    def setup(cls, context):
        """
        A potentially expensive method that may allow an :class:`Hypothesis`
        to find devices more rapidly or to find a device that it would
        otherwise miss.

        :param Context context: the pyudev context
        """
        pass

    @classmethod
    def get_devices(cls, context, value):
        """
        Get any devices that may correspond to the given string.

        :param Context context: the pyudev context
        :param str value: the value to look for
        :returns: a list of devices obtained
        :rtype: set of :class:`Device`
        """
        key = cls.match(value)
        return cls.lookup(context, key) if key is not None else frozenset()


class DeviceNumberHypothesis(Hypothesis):
    """
    Represents the hypothesis that the device is a device number.

    The device may be separated into major/minor number or a composite number.
    """

    @classmethod
    def _match_major_minor(cls, value):
        """
        Match the number under the assumption that it is a major,minor pair.

        :param str value: value to match
        :returns: the device number or None
        :rtype: int or NoneType
        """
        major_minor_re = re.compile(r"^(?P<major>\d+)(\D+)(?P<minor>\d+)$")
        match = major_minor_re.match(value)
        return match and os.makedev(
            int(match.group("major")), int(match.group("minor"))
        )

    @classmethod
    def _match_number(cls, value):
        """
        Match the number under the assumption that it is a single number.

        :param str value: value to match
        :returns: the device number or None
        :rtype: int or NoneType
        """
        number_re = re.compile(r"^(?P<number>\d+)$")
        match = number_re.match(value)
        return match and int(match.group("number"))

    @classmethod
    def match(cls, value):
        """
        Match the number under the assumption that it is a device number.

        :returns: the device number or None
        :rtype: int or NoneType
        """
        return cls._match_major_minor(value) or cls._match_number(value)

    @classmethod
    def find_subsystems(cls, context):
        """
        Find subsystems in /sys/dev.

        :param Context context: the context
        :returns: a lis of available subsystems
        :rtype: list of str
        """
        sys_path = context.sys_path
        return os.listdir(os.path.join(sys_path, "dev"))

    @classmethod
    def lookup(cls, context, key):
        """
        Lookup by the device number.

        :param Context context: the context
        :param int key: the device number
        :returns: a list of matching devices
        :rtype: frozenset of :class:`Device`
        """
        func = wrap_exception(Devices.from_device_number)
        res = (func(context, s, key) for s in cls.find_subsystems(context))
        return frozenset(r for r in res if r is not None)


class DevicePathHypothesis(Hypothesis):
    """
    Discover the device assuming the identifier is a device path.
    """

    @classmethod
    def match(cls, value):
        """
        Match ``value`` under the assumption that it is a device path.

        :returns: the device path or None
        :rtype: str or NoneType
        """
        return value

    @classmethod
    def lookup(cls, context, key):
        """
        Lookup by the path.

        :param Context context: the context
        :param str key: the device path
        :returns: a list of matching devices
        :rtype: frozenset of :class:`Device`
        """
        res = wrap_exception(Devices.from_path)(context, key)
        return frozenset((res,)) if res is not None else frozenset()


class DeviceNameHypothesis(Hypothesis):
    """
    Discover the device assuming the input is a device name.

    Try every available subsystem.
    """

    @classmethod
    def find_subsystems(cls, context):
        """
        Find all subsystems in sysfs.

        :param Context context: the context
        :rtype: frozenset
        :returns: subsystems in sysfs
        """
        sys_path = context.sys_path
        dirnames = ("bus", "class", "subsystem")
        absnames = (os.path.join(sys_path, name) for name in dirnames)
        realnames = (d for d in absnames if os.path.isdir(d))
        return frozenset(n for d in realnames for n in os.listdir(d))

    @classmethod
    def match(cls, value):
        """
        Match ``value`` under the assumption that it is a device name.

        :returns: the device path or None
        :rtype: str or NoneType
        """
        return value

    @classmethod
    def lookup(cls, context, key):
        """
        Lookup by the path.

        :param Context context: the context
        :param str key: the device path
        :returns: a list of matching devices
        :rtype: frozenset of :class:`Device`
        """
        func = wrap_exception(Devices.from_name)
        res = (func(context, s, key) for s in cls.find_subsystems(context))
        return frozenset(r for r in res if r is not None)


class DeviceFileHypothesis(Hypothesis):
    """
    Discover the device assuming the value is some portion of a device file.

    The device file may be a link to a device node.
    """

    _LINK_DIRS = [
        "/dev",
        "/dev/disk/by-id",
        "/dev/disk/by-label",
        "/dev/disk/by-partlabel",
        "/dev/disk/by-partuuid",
        "/dev/disk/by-path",
        "/dev/disk/by-uuid",
        "/dev/input/by-path",
        "/dev/mapper",
        "/dev/md",
        "/dev/vg",
    ]

    @classmethod
    def get_link_dirs(cls, context):
        """
        Get all directories that may contain links to device nodes.

        This method checks the device links of every device, so it is very
        expensive.

        :param Context context: the context
        :returns: a sorted list of directories that contain device links
        :rtype: list
        """
        devices = context.list_devices()
        devices_with_links = (d for d in devices if list(d.device_links))
        links = (l for d in devices_with_links for l in d.device_links)
        return sorted(set(os.path.dirname(l) for l in links))

    @classmethod
    def setup(cls, context):
        """
        Set the link directories to be used when discovering by file.

        Uses `get_link_dirs`, so is as expensive as it is.

        :param Context context: the context
        """
        cls._LINK_DIRS = cls.get_link_dirs(context)

    @classmethod
    def match(cls, value):
        return value

    @classmethod
    def lookup(cls, context, key):
        """
        Lookup the device under the assumption that the key is part of
        the name of a device file.

        :param Context context: the context
        :param str key: a portion of the device file name

        It is assumed that either it is the whole name of the device file
        or it is the basename.

        A device file may be a device node or a device link.
        """
        func = wrap_exception(Devices.from_device_file)
        if "/" in key:
            device = func(context, key)
            return frozenset((device,)) if device is not None else frozenset()

        files = (os.path.join(ld, key) for ld in cls._LINK_DIRS)
        devices = (func(context, f) for f in files)
        return frozenset(d for d in devices if d is not None)


class Discovery(object):
    # pylint: disable=too-few-public-methods
    """
    Provides discovery methods for devices.
    """

    _HYPOTHESES = [
        DeviceFileHypothesis,
        DeviceNameHypothesis,
        DeviceNumberHypothesis,
        DevicePathHypothesis,
    ]

    def __init__(self):
        self._hypotheses = self._HYPOTHESES

    def setup(self, context):
        """
        Set up individual hypotheses.

        May be an expensive call.

        :param Context context: the context
        """
        for hyp in self._hypotheses:
            hyp.setup(context)

    def get_devices(self, context, value):
        """
        Get the devices corresponding to value.

        :param Context context: the context
        :param str value: some identifier of the device
        :returns: a list of corresponding devices
        :rtype: frozenset of :class:`Device`
        """
        return frozenset(
            d for h in self._hypotheses for d in h.get_devices(context, value)
        )
07070100000034000081A40000000000000000000000016443203100001958000000000000000000000000000000000000002100000000pyudev-0.24.1/src/pyudev/glib.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""pyudev.glib
    ===========

    Glib integration.

    :class:`MonitorObserver` integrates device monitoring into the Glib
    mainloop by turing device events into Glib signals.

    :mod:`gi.repository.GLib` and :mod:`gi.repository.GObject` from PyGObject_
    must be available when importing this module. PyGtk is not required.

    .. _PyGObject: http://www.pygtk.org/

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
    .. versionadded:: 0.7

"""

# isort: THIRDPARTY
from gi.repository import GLib, GObject  # pylint: disable=import-error


class _ObserverMixin(object):
    """Mixin to provide observer behavior to the old and the new API."""

    # pylint: disable=too-few-public-methods

    def _setup_observer(self, monitor):
        # pylint: disable=attribute-defined-outside-init
        self.monitor = monitor
        self.event_source = None
        self.enabled = True

    @property
    def enabled(self):
        """
        Whether this observer is enabled or not.

        If ``True`` (the default), this observer is enabled, and emits events.
        Otherwise it is disabled and does not emit any events.

        .. versionadded:: 0.14
        """
        return self.event_source is not None

    @enabled.setter
    def enabled(self, value):
        if value and self.event_source is None:
            # pylint: disable=attribute-defined-outside-init
            # pylint: disable=no-member
            self.event_source = GLib.io_add_watch(
                self.monitor,
                GLib.PRIORITY_DEFAULT,
                GLib.IO_IN,
                self._process_udev_event,
            )
        elif not value and self.event_source is not None:
            # pylint: disable=no-member
            GLib.source_remove(self.event_source)

    def _process_udev_event(self, source, condition):
        # pylint: disable=unused-argument
        # pylint: disable=no-member
        if condition == GLib.IO_IN:
            device = self.monitor.poll(timeout=0)
            if device is not None:
                self._emit_event(device)
        return True

    def _emit_event(self, device):
        self.emit("device-event", device)


class MonitorObserver(GObject.Object, _ObserverMixin):
    # pylint: disable=too-few-public-methods
    """
    An observer for device events integrating into the :mod:`gi.repository.GLib`
    mainloop.

    This class inherits :class:`~gi.repository.GObject.Object` to turn device
    events into glib signals.

    >>> from pyudev import Context, Monitor
    >>> from pyudev.glib import MonitorObserver
    >>> context = Context()
    >>> monitor = Monitor.from_netlink(context)
    >>> monitor.filter_by(subsystem='input')
    >>> observer = MonitorObserver(monitor)
    >>> def device_event(observer, device):
    ...     print('event {0} on device {1}'.format(device.action, device))
    >>> observer.connect('device-event', device_event)
    >>> monitor.start()

    This class is a child of :class:`gi.repository.GObject.Object`.
    """

    __gsignals__ = {
        # explicitly convert the signal to str, because glib expects the
        # *native* string type of the corresponding python version as type of
        # signal name, and str() is the name of the native string type of both
        # python versions.  We could also remove the "unicode_literals" import,
        # but I don't want to make exceptions to the standard set of future
        # imports used throughout pyudev for the sake of consistency.
        str("device-event"): (
            GObject.SIGNAL_RUN_LAST,
            GObject.TYPE_NONE,
            (GObject.TYPE_PYOBJECT,),
        ),
    }

    def __init__(self, monitor):
        GObject.Object.__init__(self)
        self._setup_observer(monitor)


GObject.type_register(MonitorObserver)


class GUDevMonitorObserver(GObject.Object, _ObserverMixin):
    # pylint: disable=too-few-public-methods
    """
    An observer for device events integrating into the :mod:`gi.repository.GLib`
    mainloop.

    .. deprecated:: 0.17
       Will be removed in 1.0.  Use :class:`MonitorObserver` instead.
    """

    _action_signal_map = {
        "add": "device-added",
        "remove": "device-removed",
        "change": "device-changed",
        "move": "device-moved",
    }

    __gsignals__ = {
        str("device-event"): (
            GObject.SIGNAL_RUN_LAST,
            GObject.TYPE_NONE,
            (GObject.TYPE_STRING, GObject.TYPE_PYOBJECT),
        ),
        str("device-added"): (
            GObject.SIGNAL_RUN_LAST,
            GObject.TYPE_NONE,
            (GObject.TYPE_PYOBJECT,),
        ),
        str("device-removed"): (
            GObject.SIGNAL_RUN_LAST,
            GObject.TYPE_NONE,
            (GObject.TYPE_PYOBJECT,),
        ),
        str("device-changed"): (
            GObject.SIGNAL_RUN_LAST,
            GObject.TYPE_NONE,
            (GObject.TYPE_PYOBJECT,),
        ),
        str("device-moved"): (
            GObject.SIGNAL_RUN_LAST,
            GObject.TYPE_NONE,
            (GObject.TYPE_PYOBJECT,),
        ),
    }

    def __init__(self, monitor):
        GObject.Object.__init__(self)
        self._setup_observer(monitor)
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. " "Use pyudev.glib.MonitorObserver instead.",
            DeprecationWarning,
        )

    def _emit_event(self, device):
        self.emit("device-event", device.action, device)
        signal = self._action_signal_map.get(device.action)
        if signal is not None:
            self.emit(signal, device)


GObject.type_register(GUDevMonitorObserver)
07070100000035000081A40000000000000000000000016443203100005220000000000000000000000000000000000000002400000000pyudev-0.24.1/src/pyudev/monitor.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.monitor
    ==============

    Monitor implementation.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
import errno
import os
from functools import partial
from threading import Thread

# isort: LOCAL
from pyudev._os import pipe, poll
from pyudev._util import eintr_retry_call, ensure_byte_string
from pyudev.device import Device


class Monitor(object):
    """
    A synchronous device event monitor.

    A :class:`Monitor` objects connects to the udev daemon and listens for
    changes to the device list.  A monitor is created by connecting to the
    kernel daemon through netlink (see :meth:`from_netlink`):

    >>> from pyudev import Context, Monitor
    >>> context = Context()
    >>> monitor = Monitor.from_netlink(context)

    Once the monitor is created, you can add a filter using :meth:`filter_by()`
    or :meth:`filter_by_tag()` to drop incoming events in subsystems, which are
    not of interest to the application:

    >>> monitor.filter_by('input')

    When the monitor is eventually set up, you can either poll for events
    synchronously:

    >>> device = monitor.poll(timeout=3)
    >>> if device:
    ...     print('{0.action}: {0}'.format(device))
    ...

    Or you can monitor events asynchronously with :class:`MonitorObserver`.

    To integrate into various event processing frameworks, the monitor provides
    a :func:`selectable <select.select>` file description by :meth:`fileno()`.
    However, do *not*  read or write directly on this file descriptor.

    Instances of this class can directly be given as ``udev_monitor *`` to
    functions wrapped through :mod:`ctypes`.

    .. versionchanged:: 0.16
       Remove :meth:`from_socket()` which is deprecated, and even removed in
       recent udev versions.
    """

    def __init__(self, context, monitor_p):
        self.context = context
        self._as_parameter_ = monitor_p
        self._libudev = context._libudev
        self._started = False

    def __del__(self):
        self._libudev.udev_monitor_unref(self)

    @classmethod
    def from_netlink(cls, context, source="udev"):
        """
        Create a monitor by connecting to the kernel daemon through netlink.

        ``context`` is the :class:`Context` to use.  ``source`` is a string,
        describing the event source.  Two sources are available:

        ``'udev'`` (the default)
          Events emitted after udev as registered and configured the device.
          This is the absolutely recommended source for applications.

        ``'kernel'``
          Events emitted directly after the kernel has seen the device.  The
          device has not yet been configured by udev and might not be usable
          at all.  **Never** use this, unless you know what you are doing.

        Return a new :class:`Monitor` object, which is connected to the
        given source.  Raise :exc:`~exceptions.ValueError`, if an invalid
        source has been specified.  Raise
        :exc:`~exceptions.EnvironmentError`, if the creation of the monitor
        failed.
        """
        if source not in ("kernel", "udev"):
            raise ValueError(
                'Invalid source: {0!r}. Must be one of "udev" '
                'or "kernel"'.format(source)
            )
        monitor = context._libudev.udev_monitor_new_from_netlink(
            context, ensure_byte_string(source)
        )
        if not monitor:
            raise EnvironmentError("Could not create udev monitor")
        return cls(context, monitor)

    @property
    def started(self):
        """
        ``True``, if this monitor was started, ``False`` otherwise. Readonly.

        .. seealso:: :meth:`start()`
        .. versionadded:: 0.16
        """
        return self._started

    def fileno(self):
        """
        Return the file description associated with this monitor as integer.

        This is really a real file descriptor ;), which can be watched and
        :func:`select.select`\\ ed.
        """
        return self._libudev.udev_monitor_get_fd(self)

    def filter_by(self, subsystem, device_type=None):
        """
        Filter incoming events.

        ``subsystem`` is a byte or unicode string with the name of a
        subsystem (e.g. ``'input'``).  Only events originating from the
        given subsystem pass the filter and are handed to the caller.

        If given, ``device_type`` is a byte or unicode string specifying the
        device type.  Only devices with the given device type are propagated
        to the caller.  If ``device_type`` is not given, no additional
        filter for a specific device type is installed.

        These filters are executed inside the kernel, and client processes
        will usually not be woken up for device, that do not match these
        filters.

        .. versionchanged:: 0.15
           This method can also be after :meth:`start()` now.
        """
        subsystem = ensure_byte_string(subsystem)
        if device_type is not None:
            device_type = ensure_byte_string(device_type)
        self._libudev.udev_monitor_filter_add_match_subsystem_devtype(
            self, subsystem, device_type
        )
        self._libudev.udev_monitor_filter_update(self)

    def filter_by_tag(self, tag):
        """
        Filter incoming events by the given ``tag``.

        ``tag`` is a byte or unicode string with the name of a tag.  Only
        events for devices which have this tag attached pass the filter and are
        handed to the caller.

        Like with :meth:`filter_by` this filter is also executed inside the
        kernel, so that client processes are usually not woken up for devices
        without the given ``tag``.

        .. udevversion:: 154

        .. versionadded:: 0.9

        .. versionchanged:: 0.15
           This method can also be after :meth:`start()` now.
        """
        self._libudev.udev_monitor_filter_add_match_tag(self, ensure_byte_string(tag))
        self._libudev.udev_monitor_filter_update(self)

    def remove_filter(self):
        """
        Remove any filters installed with :meth:`filter_by()` or
        :meth:`filter_by_tag()` from this monitor.

        .. warning::

           Up to udev 181 (and possibly even later versions) the underlying
           ``udev_monitor_filter_remove()`` seems to be broken.  If used with
           affected versions this method always raises
           :exc:`~exceptions.ValueError`.

        Raise :exc:`~exceptions.EnvironmentError` if removal of installed
        filters failed.

        .. versionadded:: 0.15
        """
        self._libudev.udev_monitor_filter_remove(self)
        self._libudev.udev_monitor_filter_update(self)

    def enable_receiving(self):
        """
        Switch the monitor into listing mode.

        Connect to the event source and receive incoming events.  Only after
        calling this method, the monitor listens for incoming events.

        .. note::

           This method is implicitly called by :meth:`__iter__`.  You don't
           need to call it explicitly, if you are iterating over the
           monitor.

        .. deprecated:: 0.16
           Will be removed in 1.0. Use :meth:`start()` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use Monitor.start() instead.", DeprecationWarning
        )
        self.start()

    def start(self):
        """
        Start this monitor.

        The monitor will not receive events until this method is called. This
        method does nothing if called on an already started :class:`Monitor`.

        .. note::

           Typically you don't need to call this method. It is implicitly
           called by :meth:`poll()` and :meth:`__iter__()`.

        .. seealso:: :attr:`started`
        .. versionchanged:: 0.16
           This method does nothing if the :class:`Monitor` was already
           started.
        """
        if not self._started:
            self._libudev.udev_monitor_enable_receiving(self)
            # Force monitor FD into non-blocking mode
            pipe.set_fd_status_flag(self, os.O_NONBLOCK)
            self._started = True

    def set_receive_buffer_size(self, size):
        """
        Set the receive buffer ``size``.

        ``size`` is the requested buffer size in bytes, as integer.

        .. note::

           The CAP_NET_ADMIN capability must be contained in the effective
           capability set of the caller for this method to succeed.  Otherwise
           :exc:`~exceptions.EnvironmentError` will be raised, with ``errno``
           set to :data:`~errno.EPERM`.  Unprivileged processes typically lack
           this capability.  You can check the capabilities of the current
           process with the python-prctl_ module:

           >>> import prctl
           >>> prctl.cap_effective.net_admin

        Raise :exc:`~exceptions.EnvironmentError`, if the buffer size could not
        bet set.

        .. versionadded:: 0.13

        .. _python-prctl: http://packages.python.org/python-prctl
        """
        self._libudev.udev_monitor_set_receive_buffer_size(self, size)

    def _receive_device(self):
        """Receive a single device from the monitor.

        Return the received :class:`Device`, or ``None`` if no device could be
        received.

        """
        while True:
            try:
                device_p = self._libudev.udev_monitor_receive_device(self)
                return Device(self.context, device_p) if device_p else None
            except EnvironmentError as error:
                if error.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
                    # No data available
                    return None
                elif error.errno == errno.EINTR:
                    # Try again if our system call was interrupted
                    continue
                else:
                    raise

    def poll(self, timeout=None):
        """
        Poll for a device event.

        You can use this method together with :func:`iter()` to synchronously
        monitor events in the current thread::

           for device in iter(monitor.poll, None):
               print('{0.action} on {0.device_path}'.format(device))

        Since this method will never return ``None`` if no ``timeout`` is
        specified, this is effectively an endless loop. With
        :func:`functools.partial()` you can also create a loop that only waits
        for a specified time::

           for device in iter(partial(monitor.poll, 3), None):
               print('{0.action} on {0.device_path}'.format(device))

        This loop will only wait three seconds for a new device event. If no
        device event occurred after three seconds, the loop will exit.

        ``timeout`` is a floating point number that specifies a time-out in
        seconds. If omitted or ``None``, this method blocks until a device
        event is available. If ``0``, this method just polls and will never
        block.

        .. note::

           This method implicitly calls :meth:`start()`.

        Return the received :class:`Device`, or ``None`` if a timeout
        occurred. Raise :exc:`~exceptions.EnvironmentError` if event retrieval
        failed.

        .. seealso::

           :attr:`Device.action`
              The action that created this event.

           :attr:`Device.sequence_number`
              The sequence number of this event.

        .. versionadded:: 0.16
        """
        if timeout is not None and timeout > 0:
            # .poll() takes timeout in milliseconds
            timeout = int(timeout * 1000)
        self.start()
        if eintr_retry_call(poll.Poll.for_events((self, "r")).poll, timeout):
            return self._receive_device()
        return None

    def receive_device(self):
        """
        Receive a single device from the monitor.

        .. warning::

           You *must* call :meth:`start()` before calling this method.

        The caller must make sure, that there are events available in the
        event queue.  The call blocks, until a device is available.

        If a device was available, return ``(action, device)``.  ``device``
        is the :class:`Device` object describing the device.  ``action`` is
        a string describing the action.  Usual actions are:

        ``'add'``
          A device has been added (e.g. a USB device was plugged in)
        ``'remove'``
          A device has been removed (e.g. a USB device was unplugged)
        ``'change'``
          Something about the device changed (e.g. a device property)
        ``'online'``
          The device is online now
        ``'offline'``
          The device is offline now

        Raise :exc:`~exceptions.EnvironmentError`, if no device could be
        read.

        .. deprecated:: 0.16
           Will be removed in 1.0. Use :meth:`Monitor.poll()` instead.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use Monitor.poll() instead.", DeprecationWarning
        )
        device = self.poll()
        return device.action, device

    def __iter__(self):
        """
        Wait for incoming events and receive them upon arrival.

        This methods implicitly calls :meth:`start()`, and starts polling the
        :meth:`fileno` of this monitor.  If a event comes in, it receives the
        corresponding device and yields it to the caller.

        The returned iterator is endless, and continues receiving devices
        without ever stopping.

        Yields ``(action, device)`` (see :meth:`receive_device` for a
        description).

        .. deprecated:: 0.16
           Will be removed in 1.0. Use an explicit loop over :meth:`poll()`
           instead, or monitor asynchronously with :class:`MonitorObserver`.
        """
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. Use an explicit loop over "
            '"poll()" instead, or monitor asynchronously with '
            '"MonitorObserver".',
            DeprecationWarning,
        )
        self.start()
        while True:
            device = self.poll()
            if device is not None:
                yield device.action, device


class MonitorObserver(Thread):
    """
    An asynchronous observer for device events.

    This class subclasses :class:`~threading.Thread` class to asynchronously
    observe a :class:`Monitor` in a background thread:

    >>> from pyudev import Context, Monitor, MonitorObserver
    >>> context = Context()
    >>> monitor = Monitor.from_netlink(context)
    >>> monitor.filter_by(subsystem='input')
    >>> def print_device_event(device):
    ...     print('background event {0.action}: {0.device_path}'.format(device))
    >>> observer = MonitorObserver(monitor, callback=print_device_event, name='monitor-observer')
    >>> observer.daemon
    True
    >>> observer.start()

    In the above example, input device events will be printed in background,
    until :meth:`stop()` is called on ``observer``.

    .. note::

       Instances of this class are always created as daemon thread.  If you do
       not want to use daemon threads for monitoring, you need explicitly set
       :attr:`~threading.Thread.daemon` to ``False`` before invoking
       :meth:`~threading.Thread.start()`.

    .. seealso::

       :attr:`Device.action`
          The action that created this event.

       :attr:`Device.sequence_number`
          The sequence number of this event.

    .. versionadded:: 0.14

    .. versionchanged:: 0.15
       :meth:`Monitor.start()` is implicitly called when the thread is started.
    """

    def __init__(
        self, monitor, event_handler=None, callback=None, *args, **kwargs
    ):  # pylint: disable=keyword-arg-before-vararg
        """
        Create a new observer for the given ``monitor``.

        ``monitor`` is the :class:`Monitor` to observe. ``callback`` is the
        callable to invoke on events, with the signature ``callback(device)``
        where ``device`` is the :class:`Device` that caused the event.

        .. warning::

           ``callback`` is invoked in the observer thread, hence the observer
           is blocked while callback executes.

        ``args`` and ``kwargs`` are passed unchanged to the constructor of
        :class:`~threading.Thread`.

        .. deprecated:: 0.16
           The ``event_handler`` argument will be removed in 1.0. Use
           the ``callback`` argument instead.
        .. versionchanged:: 0.16
           Add ``callback`` argument.
        """
        if callback is None and event_handler is None:
            raise ValueError("callback missing")
        elif callback is not None and event_handler is not None:
            raise ValueError("Use either callback or event handler")

        Thread.__init__(self, *args, **kwargs)
        self.monitor = monitor
        # observer threads should not keep the interpreter alive
        self.daemon = True
        self._stop_event = None
        if event_handler is not None:
            # isort: STDLIB
            import warnings

            warnings.warn(
                '"event_handler" argument will be removed in 1.0. '
                "Use Monitor.poll() instead.",
                DeprecationWarning,
            )
            callback = lambda d: event_handler(d.action, d)
        self._callback = callback

    def start(self):
        """Start the observer thread."""
        if not self.is_alive():
            self._stop_event = pipe.Pipe.open()
        Thread.start(self)

    def run(self):
        self.monitor.start()
        notifier = poll.Poll.for_events(
            (self.monitor, "r"), (self._stop_event.source, "r")
        )
        while True:
            for file_descriptor, event in eintr_retry_call(notifier.poll):
                if file_descriptor == self._stop_event.source.fileno():
                    # in case of a stop event, close our pipe side, and
                    # return from the thread
                    self._stop_event.source.close()
                    return
                elif file_descriptor == self.monitor.fileno() and event == "r":
                    read_device = partial(
                        eintr_retry_call, self.monitor.poll, timeout=0
                    )
                    for device in iter(read_device, None):
                        self._callback(device)
                else:
                    raise EnvironmentError("Observed monitor hung up")

    def send_stop(self):
        """
        Send a stop signal to the background thread.

        The background thread will eventually exit, but it may still be running
        when this method returns.  This method is essentially the asynchronous
        equivalent to :meth:`stop()`.

        .. note::

           The underlying :attr:`monitor` is *not* stopped.
        """
        if self._stop_event is None:
            return
        with self._stop_event.sink:
            # emit a stop event to the thread
            eintr_retry_call(self._stop_event.sink.write, b"\x01")
            self._stop_event.sink.flush()

    def stop(self):
        """
        Synchronously stop the background thread.

        .. note::

           This method can safely be called from the observer thread. In this
           case it is equivalent to :meth:`send_stop()`.

        Send a stop signal to the backgroud (see :meth:`send_stop`), and waits
        for the background thread to exit (see :meth:`~threading.Thread.join`)
        if the current thread is *not* the observer thread.

        After this method returns in a thread *that is not the observer
        thread*, the ``callback`` is guaranteed to not be invoked again
        anymore.

        .. note::

           The underlying :attr:`monitor` is *not* stopped.

        .. versionchanged:: 0.16
           This method can be called from the observer thread.
        """
        self.send_stop()
        try:
            self.join()
        except RuntimeError:
            pass
07070100000036000081A40000000000000000000000016443203100000707000000000000000000000000000000000000002200000000pyudev-0.24.1/src/pyudev/pyqt4.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""
    pyudev.pyqt4
    ============

    PyQt4 integration.

    :class:`MonitorObserver` integrates device monitoring into the PyQt4\\_
    mainloop by turning device events into Qt signals.

    :mod:`PyQt4.QtCore` from PyQt4\\_ must be available when importing this
    module.

    .. _PyQt4: http://riverbankcomputing.co.uk/software/pyqt/intro

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: THIRDPARTY
from PyQt4 import QtCore  # pylint: disable=import-error

from ._qt_base import MonitorObserverGenerator, QUDevMonitorObserverGenerator

# pylint: disable=invalid-name
MonitorObserver = MonitorObserverGenerator.make_monitor_observer(
    QtCore.QObject, QtCore.pyqtSignal, QtCore.QSocketNotifier
)
"""
.. deprecated:: 0.17
   Will be removed in 1.0.  Use :class:`MonitorObserver` instead.
"""
QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer(
    QtCore.QObject, QtCore.pyqtSignal, QtCore.QSocketNotifier
)
07070100000037000081A400000000000000000000000164432031000005A8000000000000000000000000000000000000002200000000pyudev-0.24.1/src/pyudev/pyqt5.py# -*- coding: utf-8 -*-
# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.pyqt5
    ============

    PyQt5 integration.

    :class:`MonitorObserver` integrates device monitoring into the PyQt5_
    mainloop by turning device events into Qt signals.

    :mod:`PyQt5.QtCore` from PyQt5_ must be available when importing this
    module.

    .. _gPyQt5: http://riverbankcomputing.co.uk/software/pyqt/intro

    .. moduleauthor::  Tobias Gehring  <mail@tobiasgehring.de>
"""

# isort: THIRDPARTY
from PyQt5 import QtCore  # pylint: disable=import-error

from ._qt_base import MonitorObserverGenerator

# pylint: disable=invalid-name
MonitorObserver = MonitorObserverGenerator.make_monitor_observer(
    QtCore.QObject, QtCore.pyqtSignal, QtCore.QSocketNotifier
)
07070100000038000081A40000000000000000000000016443203100000707000000000000000000000000000000000000002300000000pyudev-0.24.1/src/pyudev/pyside.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""
    pyudev.pyside
    =============

    PySide integration.

    :class:`QUDevMonitorObserver` integrates device monitoring into the
    PySide\\_ mainloop by turing device events into Qt signals.

    :mod:`PySide.QtCore` from PySide\\_ must be available when importing this
    module.

    .. _PySide: http://www.pyside.org

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
    .. versionadded:: 0.6
"""

# isort: THIRDPARTY
from PySide import QtCore  # pylint: disable=import-error

from ._qt_base import MonitorObserverGenerator, QUDevMonitorObserverGenerator

# pylint: disable=invalid-name
MonitorObserver = MonitorObserverGenerator.make_monitor_observer(
    QtCore.QObject, QtCore.Signal, QtCore.QSocketNotifier
)
"""
.. deprecated:: 0.17
   Will be removed in 1.0.  Use :class:`MonitorObserver` instead.
"""
QUDevMonitorObserver = QUDevMonitorObserverGenerator.make_monitor_observer(
    QtCore.QObject, QtCore.Signal, QtCore.QSocketNotifier
)
07070100000039000081A40000000000000000000000016443203100000602000000000000000000000000000000000000002400000000pyudev-0.24.1/src/pyudev/pyside6.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""
    pyudev.pyside
    =============

    PySide integration.

    :class:`QUDevMonitorObserver` integrates device monitoring into the
    PySide\\_ mainloop by turing device events into Qt signals.

    :mod:`PySide.QtCore` from PySide\\_ must be available when importing this
    module.

    .. _PySide: http://www.pyside.org

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
    .. versionadded:: 0.6
"""

# isort: THIRDPARTY
from PySide6 import QtCore  # pylint: disable=import-error

from ._qt_base import MonitorObserverGenerator

# pylint: disable=invalid-name
MonitorObserver = MonitorObserverGenerator.make_monitor_observer(
    QtCore.QObject, QtCore.Signal, QtCore.QSocketNotifier
)
0707010000003A000081A40000000000000000000000016443203100000440000000000000000000000000000000000000002400000000pyudev-0.24.1/src/pyudev/version.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.version
    ==============

    Version information.

    .. moduleauthor::  mulhern  <amulhern@redhat.com>
"""

__version_info__ = (0, 24, 1, "")
__version__ = "%s%s" % (
    ".".join(str(x) for x in __version_info__[:3]),
    "".join(str(x) for x in __version_info__[3:]),
)
0707010000003B000081A4000000000000000000000001644320310000125B000000000000000000000000000000000000001F00000000pyudev-0.24.1/src/pyudev/wx.py# -*- coding: utf-8 -*-
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""pyudev.wx
    =========

    Wx integration.

    :class:`MonitorObserver` integrates device monitoring into the wxPython\\_
    mainloop by turing device events into wx events.

    :mod:`wx` from wxPython\\_ must be available when importing this module.

    .. _wxPython: http://wxpython.org/

    .. moduleauthor::  Tobias Eberle  <tobias.eberle@gmx.de>
    .. versionadded:: 0.14

"""

# isort: THIRDPARTY
from wx import EvtHandler, PostEvent  # pylint: disable=import-error
from wx.lib.newevent import NewEvent  # pylint: disable=import-error, no-name-in-module

# isort: LOCAL
# for some reason, pylint thinks pyudev is a third party import
import pyudev  # pylint: disable=wrong-import-order

DeviceEvent, EVT_DEVICE_EVENT = NewEvent()  # pylint: disable=invalid-name


class MonitorObserver(EvtHandler):
    """
    An observer for device events integrating into the :mod:`wx` mainloop.

    This class inherits :class:`~wx.EvtHandler` to turn device events into
    wx events:

    >>> from pyudev import Context, Monitor
    >>> from pyudev.wx import MonitorObserver
    >>> context = Context()
    >>> monitor = Monitor.from_netlink(context)
    >>> monitor.filter_by(subsystem='input')
    >>> observer = MonitorObserver(monitor)
    >>> def device_event(event):
    ...     print('action {0} on device {1}'.format(event.device.action, event.device))
    >>> observer.Bind(EVT_DEVICE_EVENT, device_event)
    >>> monitor.start()

    This class is a child of :class:`wx.EvtHandler`.

    .. versionadded:: 0.17
    """

    def __init__(self, monitor):
        EvtHandler.__init__(self)
        self.monitor = monitor
        self._observer_thread = None
        self.start()

    @property
    def enabled(self):
        """
        Whether this observer is enabled or not.

        If ``True`` (the default), this observer is enabled, and emits events.
        Otherwise it is disabled and does not emit any events.
        """
        return self._observer_thread is not None

    @enabled.setter
    def enabled(self, value):
        if value:
            self.start()
        else:
            self.stop()

    def start(self):
        """
        Enable this observer.

        Do nothing, if the observer is already enabled.
        """
        if self._observer_thread is not None:
            return
        self._observer_thread = pyudev.MonitorObserver(
            self.monitor, callback=self._emit_event, name="wx-observer-thread"
        )
        self._observer_thread.start()

    def stop(self):
        """
        Disable this observer.

        Do nothing, if the observer is already disabled.
        """
        if self._observer_thread is None:
            return
        self._observer_thread.stop()

    def _emit_event(self, device):
        PostEvent(self, DeviceEvent(device=device))


DeviceAddedEvent, EVT_DEVICE_ADDED = NewEvent()  # pylint: disable=invalid-name
DeviceRemovedEvent, EVT_DEVICE_REMOVED = NewEvent()  # pylint: disable=invalid-name
DeviceChangedEvent, EVT_DEVICE_CHANGED = NewEvent()  # pylint: disable=invalid-name
DeviceMovedEvent, EVT_DEVICE_MOVED = NewEvent()  # pylint: disable=invalid-name


class WxUDevMonitorObserver(MonitorObserver):
    """An observer for device events integrating into the :mod:`wx` mainloop.

    .. deprecated:: 0.17
       Will be removed in 1.0.  Use :class:`MonitorObserver` instead.
    """

    _action_event_map = {
        "add": DeviceAddedEvent,
        "remove": DeviceRemovedEvent,
        "change": DeviceChangedEvent,
        "move": DeviceMovedEvent,
    }

    def __init__(self, monitor):
        MonitorObserver.__init__(self, monitor)
        # isort: STDLIB
        import warnings

        warnings.warn(
            "Will be removed in 1.0. " "Use pyudev.wx.MonitorObserver instead.",
            DeprecationWarning,
        )

    def _emit_event(self, device):
        PostEvent(self, DeviceEvent(action=device.action, device=device))
        event_class = self._action_event_map.get(device.action)
        if event_class is not None:
            PostEvent(self, event_class(device=device))
0707010000003C000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001400000000pyudev-0.24.1/tests0707010000003D000081A40000000000000000000000016443203100000000000000000000000000000000000000000000002000000000pyudev-0.24.1/tests/__init__.py0707010000003E000081A40000000000000000000000016443203100000F34000000000000000000000000000000000000002200000000pyudev-0.24.1/tests/_constants.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.tests._device_tests
    ==========================

    Tests for devices.

    .. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: THIRDPARTY
import pytest
from hypothesis import strategies

# isort: LOCAL
from pyudev import Context, DeviceNotFoundError, Devices

from .utils import udev

_CONTEXT = Context()


def _check_device(device):
    """
    Check that device exists by getting it.
    """
    try:
        Devices.from_path(_CONTEXT, device.sys_path)
        return True
    except DeviceNotFoundError:
        return False


_DEVICE_DATA = [d for d in udev.DeviceDatabase.db()]
_DEVICES = [Devices.from_path(_CONTEXT, d.device_path) for d in _DEVICE_DATA]


def device_strategy(require_existing=True, filter_func=lambda x: True):
    """
    Strategy that yields filtered devices.

    The devices are filtered before being sampled to reduce the number
    of health failures.

    :param bool require_existing: at the very last, verify existance
    :param filter_func: a function to be used as a filter
    :type filter_func: Device -> bool
    """
    strategy = strategies.sampled_from(
        [x for x in _CONTEXT.list_devices() if filter_func(x)]
    )

    if require_existing:
        strategy = strategy.filter(_check_device)

    return strategy


_CONTEXT_STRATEGY = strategies.just(_CONTEXT)

_UDEV_VERSION = int(udev.UDevAdm.adm().query_udev_version())

_SUBSYSTEM_STRATEGY = device_strategy().map(lambda x: x.subsystem)

# Workaround for issue #181
_SUBSYSTEM_STRATEGY = _SUBSYSTEM_STRATEGY.filter(lambda s: s != "i2c")

_SYSNAME_STRATEGY = device_strategy().map(lambda x: x.sys_name)

_PROPERTY_STRATEGY = device_strategy().flatmap(
    lambda d: strategies.sampled_from([p for p in d.properties.items()])
)

_MATCH_PROPERTY_STRATEGY = _PROPERTY_STRATEGY.filter(lambda p: p[0][-4:] != "_ENC")

if _UDEV_VERSION <= 230:
    _MATCH_PROPERTY_STRATEGY = _MATCH_PROPERTY_STRATEGY.filter(
        lambda p: "[" not in p[1]
    )

# the attributes object for a given device
_ATTRIBUTES_STRATEGY = device_strategy().map(lambda d: d.attributes)

# an attribute key and value pair
_ATTRIBUTE_STRATEGY = _ATTRIBUTES_STRATEGY.flatmap(
    lambda attrs: strategies.sampled_from([a for a in attrs.available_attributes]).map(
        lambda key: (key, attrs.get(key))
    )
)

_ATTRIBUTE_STRATEGY = _ATTRIBUTE_STRATEGY.filter(lambda p: p[1] is not None)

if _UDEV_VERSION <= 230:
    _ATTRIBUTE_STRATEGY = _ATTRIBUTE_STRATEGY.filter(
        lambda p: not p[1].startswith(b"\\")
        and not p[1][-1:] == b" "
        and not p[1].startswith(b"[")
    )

# the tags object for a given device
_TAGS_STRATEGY = device_strategy().map(lambda d: [t for t in d.tags])

# an arbitrary tag belonging to a given device
_TAG_STRATEGY = _TAGS_STRATEGY.filter(lambda t: t != []).flatmap(
    strategies.sampled_from
)


def _UDEV_TEST(version, node=None):  # pylint: disable=invalid-name
    fmt_str = "%s: udev version must be at least %s, is %s"
    return pytest.mark.skipif(
        _UDEV_VERSION < version, reason=fmt_str % (node, version, _UDEV_VERSION)
    )
0707010000003F000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000002200000000pyudev-0.24.1/tests/_device_tests07070100000040000081A400000000000000000000000164432031000003B0000000000000000000000000000000000000002E00000000pyudev-0.24.1/tests/_device_tests/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.tests._device_tests
    ==========================

    Tests on devices.

    .. moduleauthor::  mulhern  <amulhern@redhat.com>
"""
07070100000041000081A400000000000000000000000164432031000011B2000000000000000000000000000000000000003700000000pyudev-0.24.1/tests/_device_tests/_attributes_tests.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
Tests methods belonging to Attributes class.

.. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import os
import stat

# isort: THIRDPARTY
import pytest
from hypothesis import given, settings, strategies

# isort: LOCAL
from pyudev import Devices

from ..utils import is_unicode_string
from ._device_tests import _CONTEXT_STRATEGY, _DEVICE_DATA, _DEVICES, _UDEV_TEST


class TestAttributes(object):
    """
    Test ``Attributes`` class methods.
    """

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_getitem(self, a_context, device_datum):
        """
        Test that attribute value exists and is instance of bytes.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        assert all(
            isinstance(device.attributes.get(key), bytes)
            for key in device_datum.attributes.keys()
        )

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_getitem_nonexisting(self, a_device):
        """
        Test behavior when corresponding value is non-existant.
        """
        not_key = "a non-existing attribute"
        assert a_device.attributes.get(not_key) is None
        with pytest.raises(KeyError):
            a_device.attributes.asstring(not_key)
        with pytest.raises(KeyError):
            a_device.attributes.asint(not_key)
        with pytest.raises(KeyError):
            a_device.attributes.asbool(not_key)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_non_iterable(self, a_device):
        """
        Test that the attributes object can not be iterated over.
        """
        # pylint: disable=pointless-statement
        with pytest.raises(TypeError):
            "key" in a_device.attributes
        with pytest.raises(TypeError):
            a_device.attributes["key"]

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_asstring(self, a_context, device_datum):
        """
        Test that attribute exists for actual device and is unicode.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        assert all(
            is_unicode_string(device.attributes.asstring(key))
            for key in device_datum.attributes.keys()
        )

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=10)
    def test_asint(self, a_context, device_datum):
        """
        Test that integer result is an int or ValueError raised.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        for key, value in device_datum.attributes.items():
            try:
                value = int(value)
            except ValueError:
                with pytest.raises(ValueError):
                    device.attributes.asint(key)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_asbool(self, a_context, device_datum):
        """
        Test that bool result is a bool or ValueError raised.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        for key, value in device_datum.attributes.items():
            if value in ("0", "1"):
                try:
                    assert device.attributes.asbool(key) in (False, True)
                except KeyError:
                    pass
            else:
                with pytest.raises(ValueError):
                    try:
                        device.attributes.asbool(key)
                    except KeyError:
                        pass
07070100000042000081A40000000000000000000000016443203100004E24000000000000000000000000000000000000003300000000pyudev-0.24.1/tests/_device_tests/_device_tests.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
Tests methods belonging to Device class.

.. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import gc
import operator
import os
import re
from datetime import timedelta

# isort: THIRDPARTY
import pytest
from hypothesis import given, settings, strategies

# isort: LOCAL
from pyudev import Device, Devices
from pyudev.device import Attributes, Tags

from .._constants import _CONTEXT, _CONTEXT_STRATEGY, _DEVICE_DATA, _DEVICES, _UDEV_TEST
from ..utils import is_unicode_string

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


class TestDevice(object):
    """
    Test ``Device`` methods.
    """

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_parent(self, a_device):
        assert a_device.parent is None or isinstance(a_device.parent, Device)

    _devices = [d for d in _DEVICES if d.parent is not None]

    @pytest.mark.skipif(len(_devices) == 0, reason="no device with a parent")
    @_UDEV_TEST(172, "test_child_of_parents")
    @given(strategies.sampled_from(_devices))
    @settings(max_examples=5)
    def test_child_of_parent(self, a_device):
        assert a_device in a_device.parent.children

    _devices = [d for d in _DEVICES if list(d.children)]

    @pytest.mark.skipif(len(_devices) == 0, reason="no device with a child")
    @_UDEV_TEST(172, "test_children")
    @given(strategies.sampled_from(_devices))
    @settings(max_examples=5)
    def test_children(self, a_device):
        children = list(a_device.children)
        for child in children:
            assert child != a_device
            assert a_device in child.ancestors

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_ancestors(self, a_device):
        child = a_device
        for ancestor in a_device.ancestors:
            assert ancestor == child.parent
            child = ancestor

    _devices = [d for d in _DEVICES if d.find_parent(d.subsystem) is not None]

    @pytest.mark.skipif(
        len(_devices) == 0, reason="no device with a parent in the same subsystem"
    )
    @given(strategies.sampled_from(_devices))
    @settings(max_examples=5)
    def test_find_parent(self, a_device):
        parent = a_device.find_parent(a_device.subsystem)
        assert parent.subsystem == a_device.subsystem
        assert parent in a_device.ancestors

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_find_parent_no_devtype_mock(self, a_device):
        funcname = "udev_device_get_parent_with_subsystem_devtype"
        spec = lambda d, s, t: None
        with mock.patch.object(
            a_device._libudev, funcname, autospec=spec
        ) as get_parent:
            get_parent.return_value = mock.sentinel.parent_device
            funcname = "udev_device_ref"
            spec = lambda d: None
            with mock.patch.object(
                a_device._libudev, funcname, autospec=spec
            ) as device_ref:
                device_ref.return_value = mock.sentinel.referenced_device
                parent = a_device.find_parent("subsystem")
                assert isinstance(parent, Device)
                assert parent._as_parameter_ is mock.sentinel.referenced_device
                get_parent.assert_called_once_with(a_device, b"subsystem", None)
                device_ref.assert_called_once_with(mock.sentinel.parent_device)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_find_parent_with_devtype_mock(self, a_device):
        funcname = "udev_device_get_parent_with_subsystem_devtype"
        spec = lambda d, s, t: None
        with mock.patch.object(
            a_device._libudev, funcname, autospec=spec
        ) as get_parent:
            get_parent.return_value = mock.sentinel.parent_device
            funcname = "udev_device_ref"
            spec = lambda d: None
            with mock.patch.object(
                a_device._libudev, funcname, autospec=spec
            ) as device_ref:
                device_ref.return_value = mock.sentinel.referenced_device
                parent = a_device.find_parent("subsystem", "devtype")
                assert isinstance(parent, Device)
                assert parent._as_parameter_ is mock.sentinel.referenced_device
                get_parent.assert_called_once_with(a_device, b"subsystem", b"devtype")
                device_ref.assert_called_once_with(mock.sentinel.parent_device)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_traverse(self, a_device):
        child = a_device
        for parent in pytest.deprecated_call(a_device.traverse):
            assert parent == child.parent
            child = parent

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_sys_path(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.sys_path == device_datum.sys_path
        assert is_unicode_string(device.sys_path)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_device_path(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.device_path == device_datum.device_path
        assert is_unicode_string(device.device_path)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_subsystem(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.subsystem == device_datum.properties["SUBSYSTEM"]
        assert is_unicode_string(device.subsystem)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_device_sys_name(self, a_device):
        assert a_device.sys_name.replace("/", "!") == os.path.basename(
            a_device.device_path
        )
        assert is_unicode_string(a_device.sys_name)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_sys_number(self, a_device):
        match = re.search(r"\d+$", a_device.sys_name)
        # filter out devices with completely nummeric names (first character
        # doesn't count according to the implementation of libudev)
        if match and match.start() > 1:
            assert a_device.sys_number == match.group(0)
            assert is_unicode_string(a_device.sys_name)
        else:
            assert a_device.sys_number is None

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_type(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.device_type == device_datum.properties.get("DEVTYPE")
        if device.device_type:
            assert is_unicode_string(device.device_type)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_driver(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.driver == device_datum.properties.get("DRIVER")
        if device.driver:
            assert is_unicode_string(device.driver)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_device_node(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.device_node == device_datum.device_node
        if device.device_node:
            assert is_unicode_string(device.device_node)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_device_number(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert device.device_number == device_datum.device_number

    @_UDEV_TEST(165, "test_is_initialized")
    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_is_initialized(self, a_device):
        assert isinstance(a_device.is_initialized, bool)

    @_UDEV_TEST(165, "test_is_initialized_mock")
    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_is_initialized_mock(self, a_device):
        funcname = "udev_device_get_is_initialized"
        spec = lambda d: None
        with mock.patch.object(a_device._libudev, funcname, autospec=spec) as func:
            func.return_value = False
            assert not a_device.is_initialized
            func.assert_called_once_with(a_device)

    @_UDEV_TEST(165, "test_time_since_initialized")
    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_time_since_initialized(self, a_device):
        assert isinstance(a_device.time_since_initialized, timedelta)

    @_UDEV_TEST(165, "test_time_since_initialized_mock")
    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_time_since_initialized_mock(self, a_device):
        funcname = "udev_device_get_usec_since_initialized"
        spec = lambda d: None
        with mock.patch.object(a_device._libudev, funcname, autospec=spec) as func:
            func.return_value = 100
            assert a_device.time_since_initialized.microseconds == 100
            func.assert_called_once_with(a_device)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_links(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        assert sorted(device.device_links) == sorted(device_datum.device_links)
        for link in device.device_links:
            assert is_unicode_string(link)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_action(self, a_device):
        assert a_device.action is None

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_action_mock(self, a_device):
        funcname = "udev_device_get_action"
        spec = lambda d: None
        with mock.patch.object(a_device._libudev, funcname, autospec=spec) as func:
            func.return_value = b"spam"
            assert a_device.action == "spam"
            func.assert_called_once_with(a_device)
            func.reset_mock()
            assert is_unicode_string(a_device.action)
            func.assert_called_once_with(a_device)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_sequence_number(self, a_device):
        assert a_device.sequence_number == 0

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_attributes(self, a_device):
        # see TestAttributes for complete attribute tests
        assert isinstance(a_device.attributes, Attributes)

    def test_no_leak(self):
        """
        Regression test for issue #32, modelled after the script which revealed
        this issue.

        The leak was caused by the following reference cycle between
        ``Attributes`` and ``Device``:

        Device._attributes -> Attributes.device

        https://github.com/lunaryorn/pyudev/issues/32
        """
        for _ in _CONTEXT.list_devices(subsystem="usb"):
            pass
        # make sure that no memory leaks
        assert not gc.garbage

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_tags(self, a_device):
        # see TestTags for complete tag tests
        assert isinstance(a_device.tags, Tags)

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_iteration(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        for prop in device.properties:
            assert is_unicode_string(prop)
        # test that iteration really yields all properties
        device_properties = set(device.properties)
        for prop in device_datum.properties:
            assert prop in device_properties

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=100)
    @_UDEV_TEST(230, "check exact equivalence of device_datum and device")
    def test_length(self, a_context, device_datum):
        """
        Verify that the keys in the device and in the datum are equal.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        assert frozenset(device_datum.properties.keys()) == frozenset(
            device.properties.keys()
        )

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=100)
    def test_key_subset(self, a_context, device_datum):
        """
        Verify that the device contains all the keys in the device datum.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        assert frozenset(device_datum.properties.keys()) <= frozenset(
            device.properties.keys()
        )

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=1)
    def test_getitem(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        for prop in device_datum.properties:
            if prop == "DEVLINKS":
                assert sorted(device.properties[prop].split(),) == sorted(
                    device_datum.properties[prop].split(),
                )
            elif prop == "TAGS":
                assert sorted(device.properties[prop].split(":"),) == sorted(
                    device_datum.properties[prop].split(":"),
                )
            else:
                # Do not test equality of device properties with udevadm oracle.
                # https://bugzilla.redhat.com/show_bug.cgi?id=1787089
                pass

    _device_data = [d for d in _DEVICE_DATA if "DEVNAME" in d.properties]

    @pytest.mark.skipif(
        len(_device_data) == 0, reason="no device with a DEVNAME property"
    )
    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_device_data))
    @settings(max_examples=5)
    def test_getitem_devname(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        data_devname = os.path.join(
            a_context.device_path, device_datum.properties["DEVNAME"]
        )
        device_devname = os.path.join(
            a_context.device_path, device.properties["DEVNAME"]
        )
        assert device_devname == data_devname

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_getitem_nonexisting(self, a_device):
        with pytest.raises(KeyError) as excinfo:
            # pylint: disable=pointless-statement
            a_device.properties["a non-existing property"]
        assert str(excinfo.value) == repr("a non-existing property")

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_asint(self, a_context, device_datum):
        device = Devices.from_path(a_context, device_datum.device_path)
        for prop, value in device_datum.properties.items():
            try:
                value = int(value)
            except ValueError:
                with pytest.raises(ValueError):
                    device.properties.asint(prop)
            else:
                assert device.properties.asint(prop) == value

    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_DEVICE_DATA))
    @settings(max_examples=5)
    def test_asbool(self, a_context, device_datum):
        """
        Test that values of 1 and 0 get properly interpreted as bool
        and that all other values raise a ValueError.

        :param Context a_context: libudev context
        :param device_datum: a device datum
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        for prop, value in device_datum.properties.items():
            if value == "1":
                assert device.properties.asbool(prop)
            elif value == "0":
                assert not device.properties.asbool(prop)
            else:
                with pytest.raises(ValueError) as exc_info:
                    device.properties.asbool(prop)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_hash(self, a_device):
        assert hash(a_device) == hash(a_device.device_path)
        assert hash(a_device.parent) == hash(a_device.parent)
        assert hash(a_device.parent) != hash(a_device)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_equality(self, a_device):
        assert a_device == a_device.device_path
        assert a_device == a_device
        assert a_device.parent == a_device.parent
        # pylint: disable=superfluous-parens
        assert not (a_device == a_device.parent)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_inequality(self, a_device):
        # pylint: disable=superfluous-parens
        assert not (a_device != a_device.device_path)
        assert not (a_device != a_device)
        assert not (a_device.parent != a_device.parent)
        assert a_device != a_device.parent

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=1)
    def test_device_ordering(self, a_device):
        """
        Verify that devices are incomparable.
        """
        operators = [operator.ge, operator.gt, operator.le, operator.lt]
        for comp_op in operators:
            with pytest.raises(TypeError) as exc_info:
                comp_op(a_device, a_device)
            assert str(exc_info.value) == "Device not orderable"

    _devices = [
        d
        for d in _DEVICES
        if d.device_type == "disk"
        and "ID_WWN_WITH_EXTENSION" in d.properties
        and "DM_MULTIPATH_TIMESTAMP" not in d.properties
    ]

    @pytest.mark.skipif(
        len(_devices) == 0, reason="unsafe to check ID_WWN_WITH_EXTENSION"
    )
    @given(strategies.sampled_from(_devices))
    @settings(max_examples=5)
    def test_id_wwn_with_extension(self, a_device):
        """
        Test that the ID_WWN_WITH_EXTENSION has a corresponding link.

        Assert that the device is a block device if it has an
        ID_WWN_WITH_EXTENSION property.

        Skip any multipathed paths, see:
        https://bugzilla.redhat.com/show_bug.cgi?id=1263441.
        """
        id_wwn = a_device.properties["ID_WWN_WITH_EXTENSION"]
        assert a_device.subsystem == "block"

        id_path = "/dev/disk/by-id"
        link_name = "wwn-%s" % id_wwn
        match = next((d for d in os.listdir(id_path) if d == link_name), None)
        assert match is not None

        link_path = os.path.join(id_path, match)
        link_target = os.readlink(link_path)
        target_path = os.path.normpath(os.path.join(id_path, link_target))
        assert target_path == a_device.device_node
07070100000043000081A40000000000000000000000016443203100001EA8000000000000000000000000000000000000003400000000pyudev-0.24.1/tests/_device_tests/_devices_tests.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
Tests methods belonging to Devices class.

.. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import os
import stat

# isort: THIRDPARTY
import pytest
from hypothesis import assume, given, settings

# isort: LOCAL
from pyudev import (
    DeviceNotFoundAtPathError,
    DeviceNotFoundByFileError,
    DeviceNotFoundByNameError,
    DeviceNotFoundByNumberError,
    DeviceNotFoundInEnvironmentError,
    Devices,
)

from .._constants import (
    _CONTEXT,
    _CONTEXT_STRATEGY,
    _SUBSYSTEM_STRATEGY,
    _UDEV_TEST,
    device_strategy,
)
from ..utils import failed_health_check_wrapper


class TestDevices(object):
    """
    Test ``Devices`` methods.
    """

    # pylint: disable=invalid-name

    @given(_CONTEXT_STRATEGY, device_strategy())
    @settings(max_examples=5)
    def test_from_path(self, a_context, a_device):
        """
        from_path() method yields correct device.
        """
        assert a_device == Devices.from_path(a_context, a_device.device_path)

    @given(_CONTEXT_STRATEGY, device_strategy())
    @settings(max_examples=5)
    def test_from_path_strips_leading_slash(self, a_context, a_device):
        """
        from_path() yields the same value, even if initial '/' is missing.
        """
        path = a_device.device_path
        assert Devices.from_path(a_context, path[1:]) == Devices.from_path(
            a_context, path
        )

    @given(_CONTEXT_STRATEGY, device_strategy())
    @settings(max_examples=5)
    def test_from_sys_path(self, a_context, a_device):
        """
        from_sys_path() yields the correct device.
        """
        assert a_device == Devices.from_sys_path(a_context, a_device.sys_path)

    def test_from_sys_path_device_not_found(self):
        """
        Verify that a non-existant sys_path causes an exception.
        """
        sys_path = "there_will_not_be_such_a_device"
        with pytest.raises(DeviceNotFoundAtPathError) as exc_info:
            Devices.from_sys_path(_CONTEXT, sys_path)
        error = exc_info.value
        assert error.sys_path == sys_path

    @given(_CONTEXT_STRATEGY, device_strategy())
    @settings(max_examples=5)
    def test_from_name(self, a_context, a_device):
        """
        Test that getting a new device based on the name and subsystem
        yields an equivalent device.
        """
        new_device = Devices.from_name(a_context, a_device.subsystem, a_device.sys_name)
        assert new_device == a_device

    @given(_CONTEXT_STRATEGY, _SUBSYSTEM_STRATEGY)
    @settings(max_examples=5)
    def test_from_name_no_device_in_existing_subsystem(self, a_context, subsys):
        """
        Verify that a real subsystem and non-existant name causes an
        exception to be raised.
        """
        with pytest.raises(DeviceNotFoundByNameError) as exc_info:
            Devices.from_name(a_context, subsys, "foobar")
        error = exc_info.value
        assert error.subsystem == subsys
        assert error.sys_name == "foobar"

    def test_from_name_nonexisting_subsystem(self):
        """
        Verify that a non-existant subsystem causes an exception.
        """
        with pytest.raises(DeviceNotFoundByNameError) as exc_info:
            Devices.from_name(_CONTEXT, "no_such_subsystem", "foobar")
        error = exc_info.value
        assert error.subsystem == "no_such_subsystem"
        assert error.sys_name == "foobar"

    @failed_health_check_wrapper
    @given(
        _CONTEXT_STRATEGY,
        device_strategy(filter_func=lambda x: x.device_node is not None),
    )
    @settings(max_examples=5)
    def test_from_device_number(self, a_context, a_device):
        """
        Verify that from_device_number() yields the correct device.
        """
        mode = os.stat(a_device.device_node).st_mode
        typ = "block" if stat.S_ISBLK(mode) else "char"
        device = Devices.from_device_number(a_context, typ, a_device.device_number)
        assert a_device == device

    @failed_health_check_wrapper
    @given(
        _CONTEXT_STRATEGY,
        device_strategy(filter_func=lambda x: x.device_node is not None),
    )
    @settings(max_examples=5)
    def test_from_device_number_wrong_type(self, a_context, a_device):
        """
        Verify appropriate behavior on real device number but swapped
        subsystems.
        """
        mode = os.stat(a_device.device_node).st_mode
        # deliberately use the wrong type here to cause either failure
        # or at least device mismatch
        typ = "char" if stat.S_ISBLK(mode) else "block"
        try:
            # this either fails, in which case the caught exception is
            # raised, or succeeds, but returns a wrong device
            # (device numbers are not unique across device types)
            device = Devices.from_device_number(a_context, typ, a_device.device_number)
            # if it succeeds, the resulting device must not match the
            # one, we are actually looking for!
            assert device != a_device
        except DeviceNotFoundByNumberError as error:
            # check the correctness of the exception attributes
            assert error.device_type == typ
            assert error.device_number == a_device.device_number

    def test_from_device_number_invalid_type(self):
        """
        Verify that a non-existant subsystem always results in an exception.
        """
        with pytest.raises(DeviceNotFoundByNumberError):
            Devices.from_device_number(_CONTEXT, "foobar", 100)

    @failed_health_check_wrapper
    @given(
        _CONTEXT_STRATEGY,
        device_strategy(filter_func=lambda x: x.device_node is not None),
    )
    @settings(max_examples=5)
    def test_from_device_file(self, a_context, a_device):
        """
        Verify that from_device_file() yields correct device.
        """
        device = Devices.from_device_file(a_context, a_device.device_node)
        assert a_device == device

    def test_from_device_file_no_device_file(self, tmpdir):
        """
        Verify that a file that is not a device file will cause an exception
        to be raised.
        """
        filename = tmpdir.join("test")
        filename.ensure(file=True)
        with pytest.raises(DeviceNotFoundByFileError):
            Devices.from_device_file(_CONTEXT, str(filename))

    def test_from_device_file_non_existing(self, tmpdir):
        """
        Test that an OSError is raised when constructing a ``Device`` from
        a file that does not actually exist.
        """
        filename = tmpdir.join("test_from_device_file_non_existing")
        assert not tmpdir.check(file=True)
        with pytest.raises(DeviceNotFoundByFileError):
            Devices.from_device_file(_CONTEXT, str(filename))

    @_UDEV_TEST(152, "test_from_environment")
    def test_from_environment(self):
        """
        there is no device in a standard environment
        """
        with pytest.raises(DeviceNotFoundInEnvironmentError):
            Devices.from_environment(_CONTEXT)
07070100000044000081A40000000000000000000000016443203100000BCE000000000000000000000000000000000000003100000000pyudev-0.24.1/tests/_device_tests/_tags_tests.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
Tests methods belonging to Devices class.

.. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: THIRDPARTY
import pytest
from hypothesis import given, settings, strategies

# isort: LOCAL
from pyudev import Devices

from ..utils import is_unicode_string
from ._device_tests import _CONTEXT_STRATEGY, _DEVICE_DATA, _DEVICES, _UDEV_TEST

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


class TestTags(object):
    """
    Test methods of the ``Tags`` class.
    """

    pytestmark = _UDEV_TEST(154, "TestTags")

    _device_data = [d for d in _DEVICE_DATA if d.tags]

    @pytest.mark.skipif(len(_device_data) == 0, reason="no device with tags")
    @given(_CONTEXT_STRATEGY, strategies.sampled_from(_device_data))
    @settings(max_examples=5)
    def test_iteration_and_contains(self, a_context, device_datum):
        """
        Test that iteration yields all tags and contains checks them.
        """
        device = Devices.from_path(a_context, device_datum.device_path)
        assert frozenset(device.tags) == frozenset(device_datum.tags)
        assert all(is_unicode_string(tag) for tag in device.tags)

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_iteration_mock(self, a_device):
        funcname = "udev_device_get_tags_list_entry"
        with pytest.libudev_list(a_device._libudev, funcname, [b"spam", b"eggs"]):
            tags = list(a_device.tags)
            assert tags == ["spam", "eggs"]
            func = a_device._libudev.udev_device_get_tags_list_entry
            func.assert_called_once_with(a_device)

    @_UDEV_TEST(172, "test_contans_mock")
    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=5)
    def test_contains_mock(self, a_device):
        """
        Test that ``udev_device_has_tag`` is called if available.
        """
        funcname = "udev_device_has_tag"
        spec = lambda d, t: None
        with mock.patch.object(a_device._libudev, funcname, autospec=spec) as func:
            func.return_value = 1
            assert "foo" in a_device.tags
            func.assert_called_once_with(a_device, b"foo")
07070100000045000081A400000000000000000000000164432031000004D6000000000000000000000000000000000000002000000000pyudev-0.24.1/tests/conftest.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: THIRDPARTY
import pytest

# isort: LOCAL
import pyudev

pytest_plugins = [
    str("tests.plugins.fake_monitor"),
    str("tests.plugins.mock_libudev"),
    str("tests.plugins.travis"),
]


@pytest.fixture
def context(request):
    """
    Return a useable :class:`pyudev.Context` object.
    """
    try:
        return pyudev.Context()
    except ImportError:
        pytest.skip("udev not available")
07070100000046000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001C00000000pyudev-0.24.1/tests/plugins07070100000047000081A400000000000000000000000164432031000003CF000000000000000000000000000000000000002800000000pyudev-0.24.1/tests/plugins/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.tests.plugins
    ====================

    Plugins to support the pyudev testsuite.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""
07070100000048000081A40000000000000000000000016443203100000B27000000000000000000000000000000000000002C00000000pyudev-0.24.1/tests/plugins/fake_monitor.py# -*- coding: utf-8 -*-
# Copyright (C) 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    plugins.fake_monitor
    ====================

    Provide a fake :class:`~pyudev.Monitor`.

    This fake monitor allows to trigger arbitrary events.  Use this class to
    test class building upon monitor without the need to rely on real events
    generated by privileged operations.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>
"""

# isort: STDLIB
import os
from select import select

# isort: THIRDPARTY
import pytest


class FakeMonitor(object):
    """
    A fake :class:`~pyudev.Monitor` which allows you to trigger arbitrary
    events.

    This fake monitor implements the complete :class:`~pyudev.Monitor`
    interface and works on real file descriptors so that you can
    :func:`~select.select()` the monitor.
    """

    def __init__(self, device_to_emit):
        self._event_source, self._event_sink = os.pipe()
        self.device_to_emit = device_to_emit
        self.started = False

    def trigger_event(self):
        """
        Trigger an event on clients of this monitor.
        """
        os.write(self._event_sink, b"\x01")

    def fileno(self):
        return self._event_source

    def filter_by(self, *args):
        pass

    def start(self):
        self.started = True

    def poll(self, timeout=None):
        rlist, _, _ = select([self._event_source], [], [], timeout)
        if self._event_source in rlist:
            os.read(self._event_source, 1)
            return self.device_to_emit

    def close(self):
        """
        Close sockets acquired by this monitor.
        """
        try:
            os.close(self._event_source)
        finally:
            os.close(self._event_sink)


@pytest.fixture
def fake_monitor(request):
    """
    Return a FakeMonitor, which emits the platform device as returned by
    the ``fake_monitor_device`` funcarg on all triggered actions.

    .. warning::

       To use this funcarg, you have to provide the ``fake_monitor_device``
       funcarg!
    """
    return FakeMonitor(request.getfixturevalue("fake_monitor_device"))
07070100000049000081A40000000000000000000000016443203100000D5A000000000000000000000000000000000000002C00000000pyudev-0.24.1/tests/plugins/mock_libudev.py# -*- coding: utf-8 -*-
# Copyright (C) 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""plugins.mock_libudev
    ====================

    Plugin to mock calls to libudev.

    This plugin adds :func:`libudev_list()` to the :mod:`pytest` namespace.

    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@gmail.com>

"""

# isort: STDLIB
from collections import namedtuple
from contextlib import contextmanager
from operator import attrgetter

# isort: THIRDPARTY
import pytest

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock

Node = namedtuple("Node", "name value next")


class LinkedList(object):
    """
    Linked list class to mock libudev list functions.
    """

    @classmethod
    def from_iterable(cls, iterable):
        """
        Create a list from the given ``iterable``.
        """
        next_node = None
        for item in reversed(iterable):
            if isinstance(item, tuple):
                name, value = item
            else:
                name, value = item, None
            node = Node(name, value, next_node)
            next_node = node
        return cls(next_node)

    def __init__(self, first):
        self.first = first


@contextmanager
def libudev_list(libudev, function, items):
    """
    Mock a libudev linked list::

       with pytest.libudev_list(device._libudev, 'udev_device_get_tag_list_entry', ['foo', 'bar']):
           assert list(device.tags) == ['foo', 'bar']

    ``function`` is a string containing the name of the libudev function that
    returns the list.  ``items`` is an iterable yielding items which shall be
    returned by the mocked list function.  An item in ``items`` can either be a
    tuple with two components, where the first component is the item name, and
    the second the item value, or a single element, which is the item name.
    The item value is ``None`` in this case.
    """
    functions_to_patch = [
        function,
        "udev_list_entry_get_next",
        "udev_list_entry_get_name",
        "udev_list_entry_get_value",
    ]
    mocks = dict((f, mock.DEFAULT) for f in functions_to_patch)
    with mock.patch.multiple(libudev, **mocks):
        udev_list = LinkedList.from_iterable(items)
        getattr(libudev, function).return_value = udev_list.first
        libudev.udev_list_entry_get_name.side_effect = attrgetter("name")
        libudev.udev_list_entry_get_value.side_effect = attrgetter("value")
        libudev.udev_list_entry_get_next.side_effect = attrgetter("next")
        yield


EXPOSED_FUNCTIONS = [libudev_list]


def pytest_configure():
    for f in EXPOSED_FUNCTIONS:
        setattr(pytest, f.__name__, f)
0707010000004A000081A400000000000000000000000164432031000005D8000000000000000000000000000000000000002600000000pyudev-0.24.1/tests/plugins/travis.py# -*- coding: utf-8 -*-
# Copyright (C) 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    plugins.travis
    ==============

    Support for Travis CI.
"""

# isort: STDLIB
import os

# isort: THIRDPARTY
import pytest


def is_on_travis_ci():
    """Determine whether the tests run on Travis CI.

    Return ``True``, if so, or ``False`` otherwise.

    """
    return os.environ.get("TRAVIS", "") == "true"


EXPOSED_FUNCTIONS = [is_on_travis_ci]


def pytest_configure():
    for f in EXPOSED_FUNCTIONS:
        setattr(pytest, f.__name__, f)


def pytest_runtest_setup(item):
    if not hasattr(item, "obj"):
        return
    marker = getattr(item.obj, "not_on_travis", None)
    if marker and is_on_travis_ci():
        pytest.skip("Test must not run on Travis CI")
0707010000004B000081A40000000000000000000000016443203100000D67000000000000000000000000000000000000002100000000pyudev-0.24.1/tests/test_core.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: STDLIB
import random
import syslog

# isort: FIRSTPARTY
from tests._constants import _UDEV_TEST
from tests.utils import is_unicode_string

# isort: LOCAL
from pyudev import udev_version

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


def test_udev_version():
    assert isinstance(udev_version(), int)
    # just to make sure, that udev versioning works.  pyudev itself should be
    # compatible with earlier versions of pyudev.  However, 150 is currently
    # the earliest udev release, I'm testing against (using Ubuntu 10.04)
    assert udev_version() > 150


class TestContext(object):
    def test_sys_path(self, context):
        assert is_unicode_string(context.sys_path)
        assert context.sys_path == "/sys"

    def test_device_path(self, context):
        assert is_unicode_string(context.device_path)
        assert context.device_path == "/dev"

    @_UDEV_TEST(167, "test_run_path")
    def test_run_path(self, context):
        assert is_unicode_string(context.run_path)
        assert context.run_path == "/run/udev"

    def test_log_priority_get(self, context):
        assert isinstance(context.log_priority, int)
        assert syslog.LOG_EMERG <= context.log_priority <= syslog.LOG_DEBUG

    def test_log_priority_get_mock(self, context):
        spec = lambda c: None
        funcname = "udev_get_log_priority"
        with mock.patch.object(context._libudev, funcname, autospec=spec) as func:
            func.return_value = mock.sentinel.log_priority
            assert context.log_priority is mock.sentinel.log_priority
            func.assert_called_once_with(context)

    def test_log_priority_set_mock(self, context):
        spec = lambda c, p: None
        funcname = "udev_set_log_priority"
        with mock.patch.object(context._libudev, funcname, autospec=spec) as func:
            context.log_priority = mock.sentinel.log_priority
            func.assert_called_once_with(context, mock.sentinel.log_priority)

    def test_log_priority_roundtrip(self, context):
        # FIXME: This adds UDEV_LOG properties?!
        old_priority = context.log_priority
        available_levels = [
            l
            for l in range(syslog.LOG_EMERG, syslog.LOG_DEBUG + 1)
            if l != old_priority
        ]
        new_priority = random.choice(available_levels)
        assert new_priority != old_priority
        try:
            context.log_priority = new_priority
            assert context.log_priority == new_priority
        finally:
            context.log_priority = old_priority
0707010000004C000081A400000000000000000000000164432031000005F6000000000000000000000000000000000000002300000000pyudev-0.24.1/tests/test_device.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.tests.test_device
    ========================

    Test for devices.

    .. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import gc

from ._device_tests._attributes_tests import TestAttributes
from ._device_tests._device_tests import TestDevice
from ._device_tests._devices_tests import TestDevices  # pylint: disable=unused-import
from ._device_tests._tags_tests import TestTags  # pylint: disable=unused-import


def test_garbage():
    """
    Make sure that all the device tests create no uncollectable objects.

    This test must stick at the bottom of this test file to make sure that
    ``py.test`` always executes it at last.
    """
    assert not gc.garbage
0707010000004D000081A4000000000000000000000001644320310000150D000000000000000000000000000000000000002500000000pyudev-0.24.1/tests/test_discover.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 Anne Mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    tests.test_discover
    ===================

    Tests discovering what device is meant by somewhat unspecific information.

    .. moduleauthor:: mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import os

# isort: THIRDPARTY
import pytest
from hypothesis import assume, given, settings, strategies

# isort: LOCAL
import pyudev
from pyudev import (
    DeviceFileHypothesis,
    DeviceNameHypothesis,
    DeviceNumberHypothesis,
    DevicePathHypothesis,
    Discovery,
)

_CONTEXT = pyudev.Context()
_DEVICES = [d for d in _CONTEXT.list_devices()]

NUM_TESTS = 5


class TestUtilities(object):
    """
    Some utilities used by the actual tests.
    """

    @staticmethod
    def get_device_numbers(a_device, a_string):
        """
        Get the device number from the device in a few formats.

        :param :class:`Device` a_device: the device
        :param  str a_string: a likely string between the major and minor
        :returns: a tuple of device numbers
        :rtype: tuple of str
        """
        device_number = a_device.device_number
        major_number = os.major(device_number)
        minor_number = os.minor(device_number)
        pair_number = "%s%s%s" % (major_number, a_string, minor_number)
        return (str(device_number), pair_number)

    @staticmethod
    def get_paths(a_device):
        """
        Get some variants on a device path.

        :param :class:`Device` a_device: the device
        :returns: a tuple of paths in sysfs
        :rtype: tuple of str
        """
        sys_path = a_device.sys_path
        (_, _, truncated_path) = sys_path[1:].partition("/")
        return (sys_path, truncated_path, a_device.device_path)

    @staticmethod
    def get_files(a_device):
        """
        Get a bunch of files, including device nodes and links.

        :param :class:`Device` a_device: the device
        :returns: a list of files
        :rtype: list of str
        """
        links = list(a_device.device_links)
        names = [os.path.basename(l) for l in links]
        links.extend(names)

        device_node = a_device.device_node
        if device_node:
            links.append(device_node)
        return links


class TestDiscovery(object):
    """
    Test discovery of an object from limited bits of its description.
    """

    _CONTEXT = pyudev.Context()
    _DISCOVER = Discovery()
    _DISCOVER.setup(_CONTEXT)

    @given(
        strategies.sampled_from(_DEVICES).filter(lambda x: x.device_number),
        strategies.text(":, -/+=").filter(lambda x: x),
    )
    @settings(max_examples=NUM_TESTS)
    def test_device_number(self, a_device, a_string):
        """
        Test lookup by a device number.

        Note that device number is per class, so there may be two
        devices with the same device number.
        """
        for number in TestUtilities.get_device_numbers(a_device, a_string):
            res = DeviceNumberHypothesis.get_devices(self._CONTEXT, number)
            assert a_device in res

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=NUM_TESTS)
    def test_path(self, a_device):
        """
        Test lookup by path.
        """
        for path in TestUtilities.get_paths(a_device):
            res = DevicePathHypothesis.get_devices(self._CONTEXT, path)
            assert res == set((a_device,))

    @given(strategies.sampled_from(_DEVICES))
    @settings(max_examples=NUM_TESTS)
    def test_name(self, a_device):
        """
        Test lookup by device name.

        Note that there may be multiple devices corresponding to the name
        in different subsystems.
        """
        name = a_device.sys_name
        res = DeviceNameHypothesis.get_devices(self._CONTEXT, name)
        assert a_device in res

    _devices = [d for d in _DEVICES if list(d.device_links)]

    @given(
        strategies.sampled_from(_DEVICES),
        strategies.text(":, -/+=").filter(lambda x: x),
    )
    @settings(max_examples=NUM_TESTS)
    def test_anything(self, a_device, a_string):
        """
        Grab any of the likely candidates for looking up a device.
        """
        assume(not "DM_MULTIPATH_TIMESTAMP" in a_device.properties)

        values = list(TestUtilities.get_device_numbers(a_device, a_string))
        values.extend(TestUtilities.get_paths(a_device))
        values.append(a_device.sys_name)
        values.extend(TestUtilities.get_files(a_device))

        results = frozenset(
            d for v in values for d in self._DISCOVER.get_devices(self._CONTEXT, v)
        )

        assert a_device in results
0707010000004E000081A40000000000000000000000016443203100002407000000000000000000000000000000000000002600000000pyudev-0.24.1/tests/test_enumerate.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: THIRDPARTY
from hypothesis import given, settings, strategies

from ._constants import (
    _ATTRIBUTE_STRATEGY,
    _CONTEXT_STRATEGY,
    _MATCH_PROPERTY_STRATEGY,
    _SUBSYSTEM_STRATEGY,
    _SYSNAME_STRATEGY,
    _TAG_STRATEGY,
    _UDEV_TEST,
    device_strategy,
)
from .utils import failed_health_check_wrapper

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


def _is_int(value):
    try:
        int(value)
        return True
    except (TypeError, ValueError):
        return False


def _is_bool(value):
    try:
        return int(value) in (0, 1)
    except (TypeError, ValueError):
        return False


def _test_direct_and_complement(context, devices, func):
    """
    Test that results are correct and that complement holds.

    :param Context context: the libudev context
    :param devices: the devices that match
    :type devices: frozenset of Device
    :param func: the property to test
    :type func: device -> bool
    """
    assert [device for device in devices if not func(device)] == []
    complement = frozenset(context.list_devices()) - devices
    assert [device for device in complement if func(device)] == []


def _test_intersection_and_union(context, matches, nomatches):
    """
    Test that intersection is empty and union is all of devices.

    :param matches: the matching devices
    :type matches: frozenset of Device
    :param nomatches: the non-matching devices
    :type nomatches: frozenset of Device
    """
    assert matches & nomatches == frozenset()
    assert matches | nomatches == frozenset(context.list_devices())


class TestEnumerator(object):
    """
    Test the Enumerator class.
    """

    @failed_health_check_wrapper
    @given(_CONTEXT_STRATEGY, _SUBSYSTEM_STRATEGY)
    @settings(max_examples=10)
    def test_match_subsystem(self, context, subsystem):
        """
        Subsystem match matches devices w/ correct subsystem.
        """
        _test_direct_and_complement(
            context,
            frozenset(context.list_devices().match_subsystem(subsystem)),
            lambda d: d.subsystem == subsystem,
        )

    @failed_health_check_wrapper
    @given(_CONTEXT_STRATEGY, _SUBSYSTEM_STRATEGY)
    @settings(max_examples=5)
    def test_match_subsystem_nomatch_unfulfillable(self, context, subsystem):
        """
        Combining match and no match should give an empty result.
        """
        devices = context.list_devices()
        devices.match_subsystem(subsystem)
        devices.match_subsystem(subsystem, nomatch=True)
        assert not list(devices)

    @failed_health_check_wrapper
    @given(_CONTEXT_STRATEGY, _SUBSYSTEM_STRATEGY)
    @settings(max_examples=1)
    def test_match_subsystem_nomatch_complete(self, context, subsystem):
        """
        Test that w/ respect to the universe of devices returned by
        list_devices() a match and its inverse are complements of each other.

        Note that list_devices() omits devices that have no subsystem,
        so with respect to the whole universe of devices, the two are
        not complements of each other.
        """
        m_devices = frozenset(context.list_devices().match_subsystem(subsystem))
        nm_devices = frozenset(
            context.list_devices().match_subsystem(subsystem, nomatch=True)
        )
        _test_intersection_and_union(context, m_devices, nm_devices)

    @failed_health_check_wrapper
    @given(_CONTEXT_STRATEGY, _MATCH_PROPERTY_STRATEGY.filter(lambda x: _is_bool(x[1])))
    @settings(max_examples=10)
    def test_match_property_bool(self, context, pair):
        """
        Verify that a probably boolean property lookup works.
        """
        key, value = pair
        bool_value = True if int(value) == 1 else False
        devices = context.list_devices().match_property(key, bool_value)
        assert all(
            device.properties[key] == value
            and device.properties.asbool(key) == bool_value
            for device in devices
        )

    @failed_health_check_wrapper
    @given(
        _CONTEXT_STRATEGY, device_strategy(filter_func=lambda d: d.parent is not None)
    )
    @settings(max_examples=5)
    def test_match_parent(self, context, device):
        """
        For a given device, verify that it is in its parent's children.

        Verify that the parent is also among the device's children,
        unless the parent lacks a subsystem

        See: rhbz#1255191
        """
        parent = device.parent
        children = list(context.list_devices().match_parent(parent))
        assert device in children


class TestEnumeratorMatchCombinations(object):
    """
    Test combinations of matches.
    """

    @given(
        _CONTEXT_STRATEGY,
        _SUBSYSTEM_STRATEGY,
        _SYSNAME_STRATEGY,
        _MATCH_PROPERTY_STRATEGY,
    )
    @settings(max_examples=10)
    def test_match(self, context, subsystem, sysname, ppair):
        """
        Test that matches from different categories are a conjunction.
        """
        prop_name, prop_value = ppair
        kwargs = {prop_name: prop_value}
        devices = frozenset(
            context.list_devices().match(
                subsystem=subsystem, sys_name=sysname, **kwargs
            )
        )
        _test_direct_and_complement(
            context,
            devices,
            lambda d: d.subsystem == subsystem
            and d.sys_name == sysname
            and d.properties.get(prop_name) == prop_value,
        )


class TestEnumeratorMatchMethod(object):
    """
    Test the behavior of Enumerator.match.

    Only methods that test behavior of this method by patching the Enumerator
    object with the methods that match() should invoke belong here.
    """

    _ENUMERATOR_STRATEGY = _CONTEXT_STRATEGY.map(lambda x: x.list_devices())

    @given(_ENUMERATOR_STRATEGY)
    @settings(max_examples=1)
    def test_match_passthrough_subsystem(self, enumerator):
        """
        Test that special keyword subsystem results in a match_subsystem call.
        """
        with mock.patch.object(
            enumerator, "match_subsystem", autospec=True
        ) as match_subsystem:
            enumerator.match(subsystem=mock.sentinel.subsystem)
            match_subsystem.assert_called_with(mock.sentinel.subsystem)

    @given(_ENUMERATOR_STRATEGY)
    @settings(max_examples=1)
    def test_match_passthrough_sys_name(self, enumerator):
        """
        Test that special keyword sys_name results in a match_sys_name call.
        """
        with mock.patch.object(
            enumerator, "match_sys_name", autospec=True
        ) as match_sys_name:
            enumerator.match(sys_name=mock.sentinel.sys_name)
            match_sys_name.assert_called_with(mock.sentinel.sys_name)

    @given(_ENUMERATOR_STRATEGY)
    @settings(max_examples=1)
    def test_match_passthrough_tag(self, enumerator):
        """
        Test that special keyword tag results in a match_tag call.
        """
        with mock.patch.object(enumerator, "match_tag", autospec=True) as match_tag:
            enumerator.match(tag=mock.sentinel.tag)
            match_tag.assert_called_with(mock.sentinel.tag)

    @_UDEV_TEST(172, "test_match_passthrough_parent")
    @given(_ENUMERATOR_STRATEGY)
    @settings(max_examples=1)
    def test_match_passthrough_parent(self, enumerator):
        """
        Test that special keyword 'parent' results in a match parent call.
        """
        with mock.patch.object(
            enumerator, "match_parent", autospec=True
        ) as match_parent:
            enumerator.match(parent=mock.sentinel.parent)
            match_parent.assert_called_with(mock.sentinel.parent)

    @given(_ENUMERATOR_STRATEGY)
    @settings(max_examples=1)
    def test_match_passthrough_property(self, enumerator):
        """
        Test that non-special keyword args are treated as properties.
        """
        with mock.patch.object(
            enumerator, "match_property", autospec=True
        ) as match_property:
            enumerator.match(eggs=mock.sentinel.eggs, spam=mock.sentinel.spam)
            assert match_property.call_count == 2
            posargs = [args for args, _ in match_property.call_args_list]
            assert ("spam", mock.sentinel.spam) in posargs
            assert ("eggs", mock.sentinel.eggs) in posargs
0707010000004F000081A4000000000000000000000001644320310000304B000000000000000000000000000000000000002400000000pyudev-0.24.1/tests/test_monitor.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: STDLIB
import random
from contextlib import contextmanager
from datetime import datetime, timedelta
from select import select

# isort: THIRDPARTY
import pytest

# isort: FIRSTPARTY
from tests._constants import _UDEV_TEST
from tests.utils.udev import DeviceDatabase

# isort: LOCAL
from pyudev import Devices, Monitor, MonitorObserver

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


# many tests just consist of some monkey patching to test, that the Monitor
# class actually calls out to udev, correctly passing arguments and handling
# return value.  Actual udev calls are difficult to test, as return values
# and side effects are dynamic and environment-dependent.  It isn't
# necessary anyway, libudev can just assumed to be correct.


@pytest.fixture
def monitor(request):
    return Monitor.from_netlink(request.getfixturevalue("context"))


@pytest.fixture
def fake_monitor_device(request):
    context = request.getfixturevalue("context")
    device = random.choice(list(DeviceDatabase.db()))
    return Devices.from_path(context, device.device_path)


@contextmanager
def patch_filter_by(type):
    add_match = "udev_monitor_filter_add_match_{0}".format(type)
    filter_update = "udev_monitor_filter_update"
    with pytest.patch_libudev(add_match) as add_match:
        add_match.return_value = 0
        with pytest.patch_libudev(filter_update) as filter_update:
            filter_update.return_value = 0
            yield add_match, filter_update


class TestMonitor(object):
    def test_from_netlink_invalid_source(self, context):
        with pytest.raises(ValueError) as exc_info:
            Monitor.from_netlink(context, source="invalid_source")
        message = 'Invalid source: {0!r}. Must be one of "udev" ' 'or "kernel"'.format(
            "invalid_source"
        )
        assert str(exc_info.value) == message

    def test_from_netlink_source_udev(self, context):
        monitor = Monitor.from_netlink(context)
        assert monitor._as_parameter_
        assert not monitor.started
        monitor = Monitor.from_netlink(context, source="udev")
        assert monitor._as_parameter_
        assert not monitor.started

    def test_from_netlink_source_udev_mock(self, context):
        funcname = "udev_monitor_new_from_netlink"
        spec = lambda c, s: None
        with mock.patch.object(context._libudev, funcname, autospec=spec) as func:
            func.return_value = mock.sentinel.monitor
            monitor = Monitor.from_netlink(context)
            assert monitor._as_parameter_ is mock.sentinel.monitor
            assert not monitor.started
            func.assert_called_once_with(context, b"udev")
            func.reset_mock()
            monitor = Monitor.from_netlink(context, "udev")
            assert monitor._as_parameter_ is mock.sentinel.monitor
            assert not monitor.started
            func.assert_called_once_with(context, b"udev")

    def test_from_netlink_source_kernel(self, context):
        monitor = Monitor.from_netlink(context, source="kernel")
        assert monitor._as_parameter_
        assert not monitor.started

    def test_from_netlink_source_kernel_mock(self, context):
        funcname = "udev_monitor_new_from_netlink"
        spec = lambda c, s: None
        with mock.patch.object(context._libudev, funcname, autospec=spec) as func:
            func.return_value = mock.sentinel.monitor
            monitor = Monitor.from_netlink(context, "kernel")
            assert monitor._as_parameter_ is mock.sentinel.monitor
            assert not monitor.started
            func.assert_called_once_with(context, b"kernel")

    def test_fileno(self, monitor):
        # we can't do more than check that no exception is thrown
        monitor.fileno()

    def test_fileno_mock(self, monitor):
        funcname = "udev_monitor_get_fd"
        spec = lambda m: None
        with mock.patch.object(monitor._libudev, funcname, autospec=spec) as func:
            func.return_value = mock.sentinel.fileno
            assert monitor.fileno() is mock.sentinel.fileno
            func.assert_called_once_with(monitor)

    def test_filter_by_no_subsystem(self, monitor):
        with pytest.raises(AttributeError):
            monitor.filter_by(None)

    def test_filter_by_subsystem_no_dev_type(self, monitor):
        monitor.filter_by(b"input")
        monitor.filter_by("input")

    def test_filter_by_subsystem_no_dev_type_mock(self, monitor):
        funcname = "udev_monitor_filter_add_match_subsystem_devtype"
        spec = lambda m, s, t: None
        libudev = monitor._libudev
        with mock.patch.object(libudev, funcname, autospec=spec) as match:
            funcname = "udev_monitor_filter_update"
            spec = lambda m: None
            with mock.patch.object(libudev, funcname, autospec=spec) as update:
                monitor.filter_by("input")
                match.assert_called_once_with(monitor, b"input", None)
                update.assert_called_once_with(monitor)

    def test_filter_by_subsystem_dev_type(self, monitor):
        monitor.filter_by("input", b"usb_interface")
        monitor.filter_by("input", "usb_interface")

    def test_filter_by_subsystem_dev_type_mock(self, monitor):
        funcname = "udev_monitor_filter_add_match_subsystem_devtype"
        spec = lambda m, s, t: None
        libudev = monitor._libudev
        with mock.patch.object(libudev, funcname, autospec=spec) as match:
            funcname = "udev_monitor_filter_update"
            spec = lambda m: None
            with mock.patch.object(libudev, funcname, autospec=spec) as update:
                monitor.filter_by("input", "usb_interface")
                match.assert_called_once_with(monitor, b"input", b"usb_interface")
                update.assert_called_once_with(monitor)

    @_UDEV_TEST(154, "test_filter_by_tag")
    def test_filter_by_tag(self, monitor):
        monitor.filter_by_tag("spam")

    @_UDEV_TEST(154, "test_filter_by_tag")
    def test_filter_by_tag_mock(self, monitor):
        funcname = "udev_monitor_filter_add_match_tag"
        spec = lambda m, t: None
        libudev = monitor._libudev
        with mock.patch.object(libudev, funcname, autospec=spec) as match:
            funcname = "udev_monitor_filter_update"
            spec = lambda m: None
            with mock.patch.object(libudev, funcname, autospec=spec) as update:
                monitor.filter_by_tag("eggs")
                match.assert_called_once_with(monitor, b"eggs")
                update.assert_called_once_with(monitor)

    def test_remove_filter(self, monitor):
        """
        The underlying ``udev_monitor_filter_remove()`` is apparently broken.
        It always causes ``EINVAL`` from ``setsockopt()``. In some version
        it changed and it now raises FileNotFoundError.
        """
        with pytest.raises(Exception):
            monitor.remove_filter()

    def test_remove_filter_mock(self, monitor):
        funcname = "udev_monitor_filter_remove"
        libudev = monitor._libudev
        spec = lambda m: None
        with mock.patch.object(libudev, funcname, autospec=spec) as remove:
            funcname = "udev_monitor_filter_update"
            with mock.patch.object(libudev, funcname, autospec=spec) as update:
                monitor.remove_filter()
                remove.assert_called_once_with(monitor)
                update.assert_called_once_with(monitor)

    def test_start_netlink_kernel_source(self, context):
        monitor = Monitor.from_netlink(context, source="kernel")
        assert not monitor.started
        monitor.start()
        assert monitor.started

    def test_start_mock(self, monitor):
        funcname = "udev_monitor_enable_receiving"
        spec = lambda m: None
        with mock.patch.object(monitor._libudev, funcname, autospec=spec) as func:
            assert not monitor.started
            monitor.start()
            assert monitor.started
            monitor.start()
            func.assert_called_once_with(monitor)

    def test_enable_receiving(self, monitor):
        """
        Test that enable_receiving() is deprecated and calls out to start().
        """
        with mock.patch.object(monitor, "start") as start:
            pytest.deprecated_call(monitor.enable_receiving)
            assert start.called

    def test_set_receive_buffer_size_mock(self, monitor):
        funcname = "udev_monitor_set_receive_buffer_size"
        spec = lambda m, s: None
        with mock.patch.object(monitor._libudev, funcname, autospec=spec) as func:
            monitor.set_receive_buffer_size(1000)
            func.assert_called_once_with(monitor, 1000)

    def test_poll_timeout(self, monitor):
        assert monitor.poll(timeout=0) is None
        now = datetime.now()
        assert monitor.poll(timeout=1) is None
        assert datetime.now() - now >= timedelta(seconds=1)

    def test_receive_device(self, monitor):
        """
        Test that Monitor.receive_device is deprecated and calls out to
        poll(), which in turn is tested by test_poll.
        """
        with mock.patch.object(monitor, "poll") as poll:
            device = mock.Mock(name="device")
            device.action = "spam"
            poll.return_value = device
            event = pytest.deprecated_call(monitor.receive_device)
            assert event[0] == "spam"
            assert event[1] is device


class TestMonitorObserver(object):
    def callback(self, device):
        self.events.append(device)
        if len(self.events) >= 2:
            self.observer.send_stop()

    def event_handler(self, action, device):
        self.events.append((action, device))
        if len(self.events) >= 2:
            self.observer.send_stop()

    def make_observer(self, monitor, use_deprecated=False):
        if use_deprecated:
            if pytest.__version__ == "2.8.4":
                self.observer = MonitorObserver(
                    monitor, event_handler=self.event_handler
                )
            else:
                self.observer = pytest.deprecated_call(
                    MonitorObserver, monitor, event_handler=self.event_handler
                )
        else:
            self.observer = MonitorObserver(monitor, callback=self.callback)
        return self.observer

    def setup(self):
        self.events = []

    def teardown(self):
        self.events = None

    def test_deprecated_handler(self, fake_monitor, fake_monitor_device):
        observer = self.make_observer(fake_monitor, use_deprecated=True)
        observer.start()
        fake_monitor.trigger_event()
        fake_monitor.trigger_event()
        # wait a second for the tests to finish, and kill the observer if
        # it is still alive then
        observer.join(1)
        if observer.is_alive():
            observer.stop()
        assert not observer.is_alive()
        assert self.events == [(None, fake_monitor_device)] * 2

    def test_fake(self, fake_monitor, fake_monitor_device):
        observer = self.make_observer(fake_monitor)
        observer.start()
        fake_monitor.trigger_event()
        fake_monitor.trigger_event()
        # wait a second for the tests to finish
        observer.join(1)
        # forcibly quit the thread if it is still alive
        if observer.is_alive():
            observer.stop()
        assert not observer.is_alive()
        # check that we got two events
        assert self.events == [fake_monitor_device] * 2
07070100000050000081A40000000000000000000000016443203100001AB8000000000000000000000000000000000000002500000000pyudev-0.24.1/tests/test_observer.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: STDLIB
import random

# isort: THIRDPARTY
import pytest

# isort: FIRSTPARTY
from tests.utils.udev import DeviceDatabase

# isort: LOCAL
from pyudev import Devices, Monitor

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


@pytest.fixture
def monitor(request):
    return Monitor.from_netlink(request.getfixturevalue("context"))


@pytest.fixture
def fake_monitor_device(request):
    context = request.getfixturevalue("context")
    device = random.choice(list(DeviceDatabase.db()))
    return Devices.from_path(context, device.device_path)


def test_fake_monitor(fake_monitor, fake_monitor_device):
    """
    Test the fake monitor just to make sure, that it works.
    """
    assert fake_monitor.poll(timeout=0) is None
    fake_monitor.trigger_event()
    device = fake_monitor.poll()
    assert device == fake_monitor_device


class ObserverTestBase(object):
    def setup_method(self, method):
        self.observer = None
        self.no_emitted_signals = 0
        self.setup()

    def teardown_method(self, method):
        if self.observer is not None:
            self.destroy_observer()
        self.teardown()

    def setup(self):
        pass

    def teardown(self):
        pass

    def destroy_observer(self):
        self.observer.enabled = False

    def create_observer(self, monitor):
        raise NotImplementedError()

    def create_event_loop(self, self_stop_timeout=5000):
        raise NotImplementedError()

    def start_event_loop(self, start_callback):
        raise NotImplementedError()

    def stop_event_loop(self):
        raise NotImplementedError()

    def connect_signal(self, callback, action=None):
        raise NotImplementedError()

    def prepare_test(self, monitor):
        self.create_event_loop(self_stop_timeout=5000)
        self.create_observer(monitor)

    def test_monitor(self, fake_monitor):
        self.prepare_test(fake_monitor)
        # test that the monitor attribute is correct
        assert self.observer.monitor is fake_monitor

    def test_events_fake_monitor(self, fake_monitor, fake_monitor_device):
        self.prepare_test(fake_monitor)
        event_callback = mock.Mock(side_effect=lambda *args: self.stop_event_loop())
        self.connect_signal(event_callback)

        self.start_event_loop(fake_monitor.trigger_event)
        event_callback.assert_called_with(fake_monitor_device)


class QtObserverTestBase(ObserverTestBase):
    def setup(self):
        self.qtcore = pytest.importorskip("{0}.QtCore".format(self.BINDING_NAME))

    def create_observer(self, monitor):
        name = self.BINDING_NAME.lower()
        mod = __import__("pyudev.{0}".format(name), None, None, [name])
        self.observer = mod.MonitorObserver(monitor)

    def connect_signal(self, callback):
        self.observer.deviceEvent.connect(callback)

    def create_event_loop(self, self_stop_timeout=5000):
        self.app = self.qtcore.QCoreApplication.instance()
        if not self.app:
            self.app = self.qtcore.QCoreApplication([])
        self.qtcore.QTimer.singleShot(self_stop_timeout, self.stop_event_loop)

    def start_event_loop(self, start_callback):
        self.qtcore.QTimer.singleShot(0, start_callback)
        self.app.exec_()

    def stop_event_loop(self):
        self.app.quit()


class TestPysideObserver(QtObserverTestBase):
    BINDING_NAME = "PySide"


class TestPyQt4Observer(QtObserverTestBase):
    BINDING_NAME = "PyQt4"


class TestPyQt5Observer(QtObserverTestBase):
    BINDING_NAME = "PyQt5"


class TestGlibObserver(ObserverTestBase):
    def setup(self):
        self.event_sources = []
        pytest.importorskip("gi")
        self.glib = pytest.importorskip("gi.repository.GLib")

    def teardown(self):
        for source in self.event_sources:
            self.glib.source_remove(source)

    def create_observer(self, monitor):
        # isort: LOCAL
        from pyudev.glib import MonitorObserver

        self.observer = MonitorObserver(monitor)

    def connect_signal(self, callback):
        # drop the sender argument from glib signal connections
        def _wrapper(obj, *args, **kwargs):
            return callback(*args, **kwargs)

        self.observer.connect("device-event", _wrapper)

    def create_event_loop(self, self_stop_timeout=5000):
        self.mainloop = self.glib.MainLoop()
        self.event_sources.append(
            self.glib.timeout_add(self_stop_timeout, self.stop_event_loop)
        )

    def start_event_loop(self, start_callback):
        def _wrapper(*args, **kwargs):
            start_callback(*args, **kwargs)
            return False

        self.event_sources.append(self.glib.timeout_add(0, _wrapper))
        self.mainloop.run()

    def stop_event_loop(self):
        self.mainloop.quit()
        return False


@pytest.mark.skipif(
    str('"DISPLAY" not in os.environ'), reason="Display required for wxPython"
)
class TestWxObserver(ObserverTestBase):
    def setup(self):
        self.wx = pytest.importorskip("wx")

    def create_observer(self, monitor):
        # isort: LOCAL
        from pyudev import wx

        self.observer = wx.MonitorObserver(monitor)

    def connect_signal(self, callback):
        # isort: LOCAL
        from pyudev.wx import EVT_DEVICE_EVENT

        def _wrapper(event):
            return callback(event.device)

        self.observer.Bind(EVT_DEVICE_EVENT, _wrapper)

    def create_event_loop(self, self_stop_timeout=5000):
        self.app = self.wx.App(False)
        # need to create a dummy Frame to get the mainloop running. Praise the
        # broken wx API…
        self.app.frame = self.wx.Frame(None)
        timer = self.wx.PyTimer(self.stop_event_loop)
        timer.Start(self_stop_timeout, True)

    def start_event_loop(self, start_callback):
        timer = self.wx.PyTimer(start_callback)
        timer.Start(0, True)
        self.app.MainLoop()

    def stop_event_loop(self):
        self.app.Exit()
07070100000051000081A40000000000000000000000016443203100002080000000000000000000000000000000000000003000000000pyudev-0.24.1/tests/test_observer_deprecated.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: THIRDPARTY
import pytest

# isort: LOCAL
from pyudev import Devices, Monitor

try:
    # isort: STDLIB
    from unittest import mock
except ImportError:
    # isort: THIRDPARTY
    import mock


@pytest.fixture
def monitor(request):
    return Monitor.from_netlink(request.getfixturevalue("context"))


@pytest.fixture
def fake_monitor_device(request):
    context = request.getfixturevalue("context")
    return Devices.from_path(context, "/devices/platform")


ACTIONS = ("add", "remove", "change", "move")


class DeprecatedObserverTestBase(object):
    def setup_method(self, method):
        self.observer = None
        self.no_emitted_signals = 0
        self.setup()

    def teardown_method(self, method):
        if self.observer is not None:
            self.destroy_observer()
        self.teardown()

    def setup(self):
        pass

    def teardown(self):
        pass

    def destroy_observer(self):
        self.observer.enabled = False

    def create_observer(self, monitor):
        raise NotImplementedError()

    def create_event_loop(self, self_stop_timeout=5000):
        raise NotImplementedError()

    def start_event_loop(self, start_callback):
        raise NotImplementedError()

    def stop_event_loop(self):
        raise NotImplementedError()

    def connect_signal(self, callback, action=None):
        raise NotImplementedError()

    def stop_when_done(self, *args, **kwargs):
        self.no_emitted_signals += 1
        if self.no_emitted_signals >= 2:
            self.stop_event_loop()

    def prepare_test(self, monitor):
        self.create_event_loop(self_stop_timeout=5000)
        self.create_observer(monitor)

    def test_monitor(self, fake_monitor):
        self.prepare_test(fake_monitor)
        # test that the monitor attribute is correct
        assert self.observer.monitor is fake_monitor

    @pytest.mark.parametrize("action", ACTIONS, ids=ACTIONS)
    def test_events_fake_monitor(self, action, fake_monitor, fake_monitor_device):
        self.prepare_test(fake_monitor)
        event_callback = mock.Mock(side_effect=self.stop_when_done)
        action_callback = mock.Mock(side_effect=self.stop_when_done)
        self.connect_signal(event_callback)
        self.connect_signal(action_callback, action=action)
        funcname = "udev_device_get_action"
        spec = lambda d: None
        with mock.patch.object(
            fake_monitor_device._libudev, funcname, autospec=spec
        ) as func:
            func.return_value = action.encode("ascii")
            self.start_event_loop(fake_monitor.trigger_event)
            func.assert_called_with(fake_monitor_device)
        event_callback.assert_called_with(action, fake_monitor_device)
        action_callback.assert_called_with(fake_monitor_device)


class DeprecatedQtObserverTestBase(DeprecatedObserverTestBase):

    ACTION_SIGNAL_MAP = {
        "add": "deviceAdded",
        "remove": "deviceRemoved",
        "change": "deviceChanged",
        "move": "deviceMoved",
    }

    def setup(self):
        self.qtcore = pytest.importorskip("{0}.QtCore".format(self.BINDING_NAME))

    def create_observer(self, monitor):
        name = self.BINDING_NAME.lower()
        mod = __import__("pyudev.{0}".format(name), None, None, [name])
        self.observer = mod.QUDevMonitorObserver(monitor)

    def connect_signal(self, callback, action=None):
        if action is None:
            self.observer.deviceEvent.connect(callback)
        else:
            signal = getattr(self.observer, self.ACTION_SIGNAL_MAP[action])
            signal.connect(callback)

    def create_event_loop(self, self_stop_timeout=5000):
        self.app = self.qtcore.QCoreApplication.instance()
        if not self.app:
            self.app = self.qtcore.QCoreApplication([])
        self.qtcore.QTimer.singleShot(self_stop_timeout, self.stop_event_loop)

    def start_event_loop(self, start_callback):
        self.qtcore.QTimer.singleShot(0, start_callback)
        self.app.exec_()

    def stop_event_loop(self):
        self.app.quit()


class TestDeprecatedPysideObserver(DeprecatedQtObserverTestBase):
    BINDING_NAME = "PySide"


class TestDeprecatedPyQt4Observer(DeprecatedQtObserverTestBase):
    BINDING_NAME = "PyQt4"


class TestDeprecatedGlibObserver(DeprecatedObserverTestBase):

    ACTION_SIGNAL_MAP = {
        "add": "device-added",
        "remove": "device-removed",
        "change": "device-changed",
        "move": "device-moved",
    }

    def setup(self):
        self.event_sources = []
        pytest.importorskip("gi")
        self.glib = pytest.importorskip("gi.repository.GLib")

    def teardown(self):
        for source in self.event_sources:
            self.glib.source_remove(source)

    def create_observer(self, monitor):
        # isort: LOCAL
        from pyudev.glib import GUDevMonitorObserver

        self.observer = GUDevMonitorObserver(monitor)

    def connect_signal(self, callback, action=None):
        # drop the sender argument from glib signal connections
        def _wrapper(obj, *args, **kwargs):
            return callback(*args, **kwargs)

        if action is None:
            self.observer.connect("device-event", _wrapper)
        else:
            self.observer.connect(self.ACTION_SIGNAL_MAP[action], _wrapper)

    def create_event_loop(self, self_stop_timeout=5000):
        self.mainloop = self.glib.MainLoop()
        self.event_sources.append(
            self.glib.timeout_add(self_stop_timeout, self.stop_event_loop)
        )

    def start_event_loop(self, start_callback):
        def _wrapper(*args, **kwargs):
            start_callback(*args, **kwargs)
            return False

        self.event_sources.append(self.glib.timeout_add(0, _wrapper))
        self.mainloop.run()

    def stop_event_loop(self):
        self.mainloop.quit()
        return False


@pytest.mark.skipif(
    str('"DISPLAY" not in os.environ'), reason="Display required for wxPython"
)
class TestDeprecatedWxObserver(DeprecatedObserverTestBase):
    def setup(self):
        self.wx = pytest.importorskip("wx")

    def create_observer(self, monitor):
        # isort: LOCAL
        from pyudev import wx

        self.observer = wx.WxUDevMonitorObserver(monitor)
        self.action_event_map = {
            "add": wx.EVT_DEVICE_ADDED,
            "remove": wx.EVT_DEVICE_REMOVED,
            "change": wx.EVT_DEVICE_CHANGED,
            "move": wx.EVT_DEVICE_MOVED,
        }

    def connect_signal(self, callback, action=None):
        if action is None:
            # isort: LOCAL
            from pyudev.wx import EVT_DEVICE_EVENT

            def _wrapper(event):
                return callback(event.action, event.device)

            self.observer.Bind(EVT_DEVICE_EVENT, _wrapper)
        else:

            def _wrapper(event):
                return callback(event.device)

            self.observer.Bind(self.action_event_map[action], _wrapper)

    def create_event_loop(self, self_stop_timeout=5000):
        self.app = self.wx.App(False)
        # need to create a dummy Frame to get the mainloop running. Praise the
        # broken wx API…
        self.app.frame = self.wx.Frame(None)
        timer = self.wx.PyTimer(self.stop_event_loop)
        timer.Start(self_stop_timeout, True)

    def start_event_loop(self, start_callback):
        timer = self.wx.PyTimer(start_callback)
        timer.Start(0, True)
        self.app.MainLoop()

    def stop_event_loop(self):
        self.app.Exit()
07070100000052000081A4000000000000000000000001644320310000095C000000000000000000000000000000000000002100000000pyudev-0.24.1/tests/test_pypi.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012, 2013 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: STDLIB
import os
import re
import subprocess
from distutils.filelist import FileList

# isort: THIRDPARTY
import py.path
import pytest

TEST_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
SOURCE_DIRECTORY = os.path.abspath(os.path.join(TEST_DIRECTORY, os.pardir))
MANIFEST = os.path.join(SOURCE_DIRECTORY, "MANIFEST.in")

# Files in the repository that don't need to be present in the sdist
REQUIRED_BLACKLIST = [r"^\.git.+", r"\.travis\.yml$", r"^MANIFEST\.in$", r"^Makefile$"]


def _get_required_files():
    if not os.path.isdir(os.path.join(SOURCE_DIRECTORY, ".git")):
        pytest.skip("Not in git clone")
    git = py.path.local.sysfind("git")
    if not git:
        pytest.skip("git not available")
    ls_files = subprocess.Popen(
        ["git", "ls-files"], cwd=SOURCE_DIRECTORY, stdout=subprocess.PIPE
    )
    output = ls_files.communicate()[0].decode("utf-8")
    for filename in output.splitlines():
        if not any(re.search(p, filename) for p in REQUIRED_BLACKLIST):
            yield filename


def _get_manifest_files():
    filelist = FileList()
    old_wd = os.getcwd()
    try:
        os.chdir(SOURCE_DIRECTORY)
        filelist.findall()
    finally:
        os.chdir(old_wd)
    with open(MANIFEST, "r", encoding="utf-8") as source:
        for line in source:
            filelist.process_template_line(line.strip())
    return filelist.files


def test_manifest_complete():
    required_files = sorted(_get_required_files())
    included_files = sorted(_get_manifest_files())
    assert required_files == included_files
07070100000053000081A40000000000000000000000016443203100001756000000000000000000000000000000000000002100000000pyudev-0.24.1/tests/test_util.py# -*- coding: utf-8 -*-
# Copyright (C) 2010, 2011, 2012 Sebastian Wiesner <lunaryorn@gmail.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

# isort: STDLIB
import sys

# isort: THIRDPARTY
import pytest
from hypothesis import given, settings, strategies

# isort: LOCAL
from pyudev import Context, _util

from .utils import is_unicode_string

try:
    # isort: STDLIB
    from unittest.mock import Mock
except ImportError:
    # isort: THIRDPARTY
    from mock import Mock


_CONTEXT = Context()


def test_ensure_byte_string():
    assert isinstance(_util.ensure_byte_string("hello world"), bytes)
    assert _util.ensure_byte_string("hello world") == b"hello world"
    hello = b"hello world"
    assert _util.ensure_byte_string(hello) is hello


def test_ensure_byte_string_none():
    with pytest.raises(AttributeError):
        _util.ensure_byte_string(None)


def test_ensure_unicode_string():
    assert is_unicode_string(_util.ensure_unicode_string(b"hello world"))
    assert _util.ensure_unicode_string(b"hello world") == "hello world"
    hello = "hello world"
    assert _util.ensure_unicode_string(hello) is hello


def test_ensure_unicode_string_none():
    with pytest.raises(AttributeError):
        _util.ensure_unicode_string(None)


def test_property_value_to_bytes_string():
    hello = "hello world".encode(sys.getfilesystemencoding())
    assert _util.property_value_to_bytes(hello) is hello
    assert isinstance(_util.property_value_to_bytes("hello world"), bytes)
    assert _util.property_value_to_bytes("hello world") == hello


def test_property_value_to_bytes_int():
    assert _util.property_value_to_bytes(10000) == b"10000"
    assert isinstance(_util.property_value_to_bytes(10000), bytes)


def test_property_value_to_bytes_bool():
    assert _util.property_value_to_bytes(True) == b"1"
    assert isinstance(_util.property_value_to_bytes(True), bytes)
    assert _util.property_value_to_bytes(False) == b"0"
    assert isinstance(_util.property_value_to_bytes(False), bytes)


def test_string_to_bool_true():
    assert isinstance(_util.string_to_bool("1"), bool)
    assert _util.string_to_bool("1")


def test_string_to_bool_false():
    assert isinstance(_util.string_to_bool("0"), bool)
    assert not _util.string_to_bool("0")


def test_string_to_bool_invalid_value():
    with pytest.raises(ValueError) as exc_info:
        _util.string_to_bool("foo")
    assert str(exc_info.value) == "Not a boolean value: {0!r}".format("foo")


def test_udev_list_iterate_no_entry():
    assert not list(_util.udev_list_iterate(Mock(), None))


def test_udev_list_iterate_mock():
    libudev = Mock(name="libudev")
    items = [("spam", "eggs"), ("foo", "bar")]
    with pytest.libudev_list(libudev, "udev_enumerate_get_list_entry", items):
        udev_list = libudev.udev_enumerate_get_list_entry()
        assert list(_util.udev_list_iterate(libudev, udev_list)) == [
            ("spam", "eggs"),
            ("foo", "bar"),
        ]


def raise_valueerror():
    raise ValueError("from function")


_CHAR_DEVICES = list(_CONTEXT.list_devices(subsystem="tty"))


@pytest.mark.skipif(len(_CHAR_DEVICES) == 0, reason="no tty devices")
@given(strategies.sampled_from(_CHAR_DEVICES))
@settings(max_examples=5)
def test_get_device_type_character_device(a_device):
    """
    Check that the device type of a character device is actually char.
    """
    assert _util.get_device_type(a_device.device_node) == "char"


_BLOCK_DEVICES = list(_CONTEXT.list_devices(subsystem="block"))


@pytest.mark.skipif(len(_BLOCK_DEVICES) == 0, reason="no block devices")
@given(strategies.sampled_from(_BLOCK_DEVICES))
@settings(max_examples=5)
def test_get_device_type_block_device(a_device):
    """
    Check that the device type of a block device is actually block.
    """
    assert _util.get_device_type(a_device.device_node) == "block"


def test_get_device_type_no_device_file(tmpdir):
    filename = tmpdir.join("test")
    filename.ensure(file=True)
    with pytest.raises(ValueError) as excinfo:
        _util.get_device_type(str(filename))
    message = "not a device file: {0!r}".format(str(filename))
    assert str(excinfo.value) == message


def test_get_device_type_not_existing(tmpdir):
    """
    Test that an OSError is raised when checking device type using a file
    that does not actually exist.
    """
    filename = tmpdir.join("test_get_device_type_not_existing")
    assert not tmpdir.check(file=True)
    with pytest.raises(OSError):
        _util.get_device_type(str(filename))


def test_eintr_retry_call(tmpdir):
    # isort: STDLIB
    import os
    import select
    import signal

    def handle_alarm(signum, frame):
        # pylint: disable=unused-argument
        pass

    orig_alarm = signal.getsignal(signal.SIGALRM)

    # Open an empty file and use it to wait for exceptions which should
    # never happen
    filename = tmpdir.join("test")
    filename.ensure(file=True)
    fd = os.open(str(filename), os.O_RDONLY)

    try:
        signal.signal(signal.SIGALRM, handle_alarm)

        # Ensure that wrapping the call does not raise EINTR
        signal.alarm(1)
        assert _util.eintr_retry_call(select.select, [], [], [3], 2) == ([], [], [])
    finally:
        os.close(fd)
        signal.signal(signal.SIGALRM, orig_alarm)
07070100000054000041ED0000000000000000000000026443203100000000000000000000000000000000000000000000001A00000000pyudev-0.24.1/tests/utils07070100000055000081A4000000000000000000000001644320310000040C000000000000000000000000000000000000002600000000pyudev-0.24.1/tests/utils/__init__.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    pyudev.tests.utils
    ==========================

    Utils to support pyudev testing.

    .. moduleauthor::  mulhern  <amulhern@redhat.com>
"""

from . import udev
from .misc import failed_health_check_wrapper, is_unicode_string
07070100000056000081A40000000000000000000000016443203100000764000000000000000000000000000000000000002200000000pyudev-0.24.1/tests/utils/misc.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    utils.misc
    =============

    Miscellaneous useful methods.

    .. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
from functools import wraps

# isort: THIRDPARTY
import pytest
from hypothesis.core import FailedHealthCheck


def is_unicode_string(value):
    """
    Return ``True``, if ``value`` is of a real unicode string type
    (``unicode`` in python 2, ``str`` in python 3), ``False`` otherwise.
    """
    return isinstance(value, str)


def failed_health_check_wrapper(func):
    """
    If the test fails a health check, calls skip().
    """

    @wraps(func)
    def the_func(*args):
        """
        Catch a hypothesis FailedHealthCheck exception and log it as a skip.
        """
        try:
            func(*args)
        except FailedHealthCheck:
            pytest.skip(
                "failed health check for %s() (%s: %s)"
                % (
                    func.__code__.co_name,
                    func.__code__.co_filename,
                    func.__code__.co_firstlineno,
                )
            )

    return the_func
07070100000057000081A400000000000000000000000164432031000026B5000000000000000000000000000000000000002200000000pyudev-0.24.1/tests/utils/udev.py# -*- coding: utf-8 -*-
# Copyright (C) 2015 mulhern <amulhern@redhat.com>

# This library is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 2.1 of the License, or (at your
# option) any later version.

# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
# for more details.

# You should have received a copy of the GNU Lesser General Public License
# along with this library; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
"""
    utils.udev
    =====================

    Provide access the udev device database.

    Parses the udev device database from :program:`udevadm`.

    .. moduleauthor::  mulhern <amulhern@redhat.com>
"""

# isort: STDLIB
import collections
import errno
import os
import re
import subprocess
import sys


class UDevAdm(object):
    """
    Wrap ``udevadm`` utility.
    """

    CANDIDATES = ["/sbin/udevadm", "udevadm"]
    _adm = None

    @classmethod
    def find(cls):
        """
        Construct a valid :class:`UDevAdm` object.

        :returns: a working :class:`UDevAdm` object
        :rtype: :class:`UDevAdm`
        :raises EnvironmentError:
        """
        for candidate in cls.CANDIDATES:
            try:
                udevadm = cls(candidate)
                # try to execute udev to make sure that it's actually
                # executable
                udevadm.query_udev_version()
                return udevadm
            except EnvironmentError as error:
                if error.errno != errno.ENOENT:
                    raise

    @classmethod
    def adm(cls):
        """
        Returns the singleton object of this class, if one can be found.

        :returns: singleton :class:`UDevAdm` object
        :rtype: :class:`UDevAdm`
        """
        if cls._adm is None:
            try:
                cls._adm = cls.find()
            except EnvironmentError:
                pass
        return cls._adm

    def __init__(self, udevadm):
        """
        Create a new ``udevadm`` wrapper for the given udevadm executable.

        ``udevadm`` is the path to udevadm as string.  If relative, ``udevadm``
        is looked up in ``$PATH``.
        """
        self.udevadm = udevadm

    def query_udev_version(self):
        """
        Return the version of udevadm.

        :returns: the udevadm version
        :rtype: int
        """
        return int(self._execute("--version"))

    def _execute(self, *args):
        """
        Execute a udevadm command.

        :raises CalledProcessError:
        """
        command = [self.udevadm] + list(args)
        proc = subprocess.Popen(command, stdout=subprocess.PIPE)
        output = proc.communicate()[0].strip()
        if proc.returncode != 0:
            raise subprocess.CalledProcessError(proc.returncode, command)
        return output

    def _execute_query(self, device_path, query_type="all"):
        """
        Execute a "udevadm info" query.

        :raises CalledProcessError:
        """
        output = self._execute(
            "info", "--root", "--path", device_path, "--query", query_type
        )
        return output.decode(sys.getfilesystemencoding())

    def query_devices(self):
        """
        Generate devices from udevadm database.

        Yields sys paths, minus the initial '/sys'.

        :raises CalledProcessError:
        """
        database = (
            self._execute("info", "--export-db")
            .decode(sys.getfilesystemencoding())
            .splitlines()
        )

        return (l[3:] for l in (l.strip() for l in database) if l[:3] == "P: ")

    def query_device_properties(self, device_path):
        """
        Return map of properties.

        :returns: a map of properties on the device
        :rtype: dict of str * str

        :raises CalledProcessError:
        """
        pairs = [
            l.strip().split("=", 1)
            for l in self._execute_query(device_path, "property").splitlines()
        ]

        if self.adm().query_udev_version() < 230:
            num_pairs = len(pairs)
            indices = [i for i in range(num_pairs) if pairs[i][1] == ""]
            pairs = [
                pairs[i]
                for i in range(num_pairs)
                if i not in indices and i - 1 not in indices
            ]

        return dict(pairs)

    def query_device_attributes(self, device_path):
        output = self._execute("info", "--attribute-walk", "--path", device_path)
        attribute_dump = output.decode(sys.getfilesystemencoding()).splitlines()
        attributes = {}
        for line in attribute_dump:
            line = line.strip()
            if line.startswith("looking at parent device"):
                # we don't continue with attributes of parent devices, we only
                # want the attributes of the given device
                break
            if line.startswith("ATTR"):
                name, value = line.split("==", 1)
                # remove quotation marks from attribute value
                value = value[1:-1]
                # remove prefix from attribute name
                name = re.search("{(.*)}", name).group(1)
                attributes[name] = value
        return attributes

    def query_device(self, device_path, query_type):
        if query_type not in ("symlink", "name"):
            raise ValueError(query_type)
        try:
            return self._execute_query(device_path, query_type)
        except subprocess.CalledProcessError:
            return None


class DeviceData(object):
    """
    Data for a single device.
    """

    def __init__(self, device_path, udevadm):
        self.device_path = device_path
        self._udevadm = udevadm

    def __repr__(self):
        return "{0}({1})".format(self.__class__.__name__, self.device_path)

    @property
    def sys_path(self):
        """
        Get the ``sysfs`` path of the device.
        """
        return "/sys" + self.device_path

    @property
    def exists(self):
        """
        Whether this device has some real existance on machine.

        :returns: True if the device does exist, otherwise False.
        :rtype: bool
        """
        return os.path.exists(self.sys_path)

    @property
    def properties(self):
        """
        Get the device properties as mapping of property names as strings to
        property values as strings.
        """
        return self._udevadm.query_device_properties(self.device_path)

    @property
    def attributes(self):
        """
        Get the device attributes as mapping of attributes names as strings to
        property values as byte strings.

        .. warning::

           As ``udevadm`` only exports printable attributes, this list can be
           incomplete!  Do *not* compare this dictionary directly to a
           attributes dictionary received through pyudev!
        """
        return self._udevadm.query_device_attributes(self.device_path)

    @property
    def tags(self):
        """
        Get the device tags as list of strings.
        """
        tags = self.properties.get("TAGS", "").split(":")
        return [t for t in tags if t]

    @property
    def device_node(self):
        """
        Get the device node path as string, or ``None`` if the device has no
        device node.
        """
        return self._udevadm.query_device(self.device_path, "name")

    @property
    def device_links(self):
        """
        Get the device links as list of strings.
        """
        links = self._udevadm.query_device(self.device_path, "symlink")
        return links.split() if links else []

    @property
    def device_number(self):
        """
        Get the device number as integer or 0 if the device has no device
        number.
        """
        device_node = self.device_node
        return 0 if device_node is None else os.stat(device_node).st_rdev


class DeviceDatabase(collections.abc.Iterable, collections.abc.Sized):
    """
    The udev device database.

    Takes a snapshot of the udev device database when it is initialized.
    Consequently, some :class:`DeviceData` objects in the database may
    not exist when the database is iterated over.

    This class is an iterable over :class:`DeviceData` objects that contain the
    data associated with each device stored in the udev database.
    """

    _db = None

    @classmethod
    def db(cls, renew=False):  # pylint: disable=invalid-name
        """
        Get a database object.

        :param bool renew: if renew is True, get a new object
        :returns: a database object
        :rtype: :class:`DeviceDatabase`
        """
        if cls._db is None or renew:
            udevadm = UDevAdm.adm()
            if udevadm:
                cls._db = DeviceDatabase(udevadm)
        return cls._db

    def __init__(self, udevadm):
        self._udevadm = udevadm
        self._devices = set(self._udevadm.query_devices())

    def __iter__(self):
        return (
            d for d in (DeviceData(d, self._udevadm) for d in self._devices) if d.exists
        )

    def __len__(self):
        return sum(1 for _ in self)

    def find_device_data(self, device_path):
        """
        Find device data for the device designated by ``device_path``.

        ``device_path`` is a string containing the device path.

        Return a :class:`DeviceData` object containing the data for the given
        device, or ``None`` if the device was not found.
        """
        data = DeviceData(device_path, self._udevadm)
        return data if data.exists and data in self else None
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!748 blocks
openSUSE Build Service is sponsored by