pyudev-0.16.1/0000755000175000001440000000000012006442367014135 5ustar swiesnerusers00000000000000pyudev-0.16.1/tox.ini0000644000175000001440000000123512006301274015440 0ustar swiesnerusers00000000000000[tox] envlist = py26,py31,doc [testenv] setenv=LD_LIBRARY_PATH={envdir}/lib downloadcache={toxworkdir}/_download deps= pytest>=2.2 mock>=1.0b1 commands= {envpython} {toxinidir}/build_bindings.py \ --download-directory={toxworkdir}/_download \ --build-directory={envtmpdir}/build_bindings py.test {posargs:--junitxml={envname}-tests.xml -rsx} [testenv:doc] downloadcache={toxworkdir}/_download deps= sphinx>=1.0.7 sphinxcontrib-issuetracker>=0.9 commands= sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees doc {envtmpdir}/linkcheck sphinx-build -W -b html -d {envtmpdir}/doctrees doc {envtmpdir}/html pyudev-0.16.1/pyudev.egg-info/0000755000175000001440000000000012006442367017143 5ustar swiesnerusers00000000000000pyudev-0.16.1/pyudev.egg-info/dependency_links.txt0000644000175000001440000000000112006442357023210 0ustar swiesnerusers00000000000000 pyudev-0.16.1/pyudev.egg-info/SOURCES.txt0000644000175000001440000000213312006442357021025 0ustar swiesnerusers00000000000000CHANGES.rst COPYING MANIFEST.in README.rst build_bindings.py requirements.txt setup.cfg setup.py tox.ini doc/changes.rst doc/conf.py doc/contribute.rst doc/guide.rst doc/index.rst doc/install.rst doc/licencing.rst doc/_templates/info.html doc/api/index.rst doc/api/pyudev.glib.rst doc/api/pyudev.pyqt4.rst doc/api/pyudev.pyside.rst doc/api/pyudev.rst doc/api/pyudev.wx.rst doc/tests/index.rst doc/tests/plugins.rst doc/tests/running.rst pyudev/__init__.py pyudev/_compat.py pyudev/_libudev.py pyudev/_qt_base.py pyudev/_util.py pyudev/core.py pyudev/device.py pyudev/glib.py pyudev/monitor.py pyudev/pyqt4.py pyudev/pyside.py pyudev/wx.py pyudev.egg-info/PKG-INFO pyudev.egg-info/SOURCES.txt pyudev.egg-info/dependency_links.txt pyudev.egg-info/top_level.txt tests/conftest.py tests/test_core.py tests/test_device.py tests/test_enumerate.py tests/test_libudev.py tests/test_monitor.py tests/test_observer.py tests/test_pypi.py tests/test_util.py tests/plugins/__init__.py tests/plugins/fake_monitor.py tests/plugins/libudev.py tests/plugins/mock_libudev.py tests/plugins/privileged.py tests/plugins/udev_database.pypyudev-0.16.1/pyudev.egg-info/top_level.txt0000644000175000001440000000000712006442357021671 0ustar swiesnerusers00000000000000pyudev pyudev-0.16.1/pyudev.egg-info/PKG-INFO0000644000175000001440000001232312006442357020240 0ustar swiesnerusers00000000000000Metadata-Version: 1.1 Name: pyudev Version: 0.16.1 Summary: A libudev binding Home-page: http://pyudev.readthedocs.org/ Author: Sebastian Wiesner Author-email: lunaryorn@gmail.com License: LGPL 2.1+ Description: ###### pyudev ###### .. image:: https://secure.travis-ci.org/lunaryorn/pyudev.png?branch=master :target: http://travis-ci.org/lunaryorn/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_ 2 (2.6 or newer) and 3 (3.1 or newer), and PyPy_ 1.5 or newer. 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 ask questions about pyudev on the mailing list at pyudev@librelist.com. To join this list, send a mail to pyudev@librelist.com and reply to the confirmation email. Please report issues 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/lunaryorn/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 `_. - Understand the `branching model `_. - Use a topic branch based on the ``develop`` branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - 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 `testsuite documentation `_). - 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. .. _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: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ .. _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/lunaryorn/pyudev/issues .. _GitHub: http://github.com/lunaryorn/pyudev .. _git: http://www.git-scm.com/ .. _pep8: http://pypi.python.org/pypi/pep8/ Platform: Linux Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Hardware Classifier: Topic :: System :: Operating System Kernels :: Linux pyudev-0.16.1/README.rst0000644000175000001440000000712012006442153015615 0ustar swiesnerusers00000000000000###### pyudev ###### .. image:: https://secure.travis-ci.org/lunaryorn/pyudev.png?branch=master :target: http://travis-ci.org/lunaryorn/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_ 2 (2.6 or newer) and 3 (3.1 or newer), and PyPy_ 1.5 or newer. 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 ask questions about pyudev on the mailing list at pyudev@librelist.com. To join this list, send a mail to pyudev@librelist.com and reply to the confirmation email. Please report issues 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/lunaryorn/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 `_. - Understand the `branching model `_. - Use a topic branch based on the ``develop`` branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - 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 `testsuite documentation `_). - 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. .. _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: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ .. _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/lunaryorn/pyudev/issues .. _GitHub: http://github.com/lunaryorn/pyudev .. _git: http://www.git-scm.com/ .. _pep8: http://pypi.python.org/pypi/pep8/ pyudev-0.16.1/COPYING0000644000175000001440000006350012006301274015163 0ustar swiesnerusers00000000000000 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. Copyright (C) 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. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! pyudev-0.16.1/MANIFEST.in0000644000175000001440000000035712006442153015671 0ustar swiesnerusers00000000000000recursive-include pyudev *.py recursive-include doc *.rst *.py *.html recursive-include tests *.py include build_bindings.py include tox.ini global-include requirements.txt include CHANGES.rst COPYING README.rst include setup.py setup.cfg pyudev-0.16.1/pyudev/0000755000175000001440000000000012006442367015451 5ustar swiesnerusers00000000000000pyudev-0.16.1/pyudev/monitor.py0000644000175000001440000004735412006301274017516 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) import os import select from threading import Thread from contextlib import closing from pyudev._libudev import libudev from pyudev._util import ensure_byte_string from pyudev.core import Device __all__ = ['Monitor', 'MonitorObserver'] 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 ` 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._started = False def __del__(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 = 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 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: device_type = ensure_byte_string(device_type) libudev.udev_monitor_filter_add_match_subsystem_devtype( self, subsystem, device_type) 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. """ libudev.udev_monitor_filter_add_match_tag( self, ensure_byte_string(tag)) 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 """ libudev.udev_monitor_filter_remove(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. """ 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: libudev.udev_monitor_enable_receiving(self) 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 """ 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`. Raise :exc:`~exceptions.EnvironmentError`, if no device could be read. """ device_p = libudev.udev_monitor_receive_device(self) if not device_p: raise EnvironmentError('Could not receive device') return Device(self.context, device_p) 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 """ rlist, _, _ = select.select([self], [], [], timeout) if self in rlist: return self._receive_device() else: 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. """ import warnings warnings.warn('Will be removed in 1.0. Use Monitor.poll() instead.', DeprecationWarning) device = self._receive_device() 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`. """ 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() with closing(select.epoll()) as notifier: notifier.register(self, select.EPOLLIN) while True: events = notifier.poll() for event in events: device = self._receive_device() 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): """ 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_source, self._stop_event_sink = os.pipe() if event_handler is not None: 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 run(self): self.monitor.start() with closing(select.epoll()) as notifier: # poll on the stop event fd notifier.register(self._stop_event_source, select.EPOLLIN) # and on the monitor notifier.register(self.monitor, select.EPOLLIN) while True: for fd, _ in notifier.poll(): if fd == self._stop_event_source: # in case of a stop event, close our pipe side, and # return from the thread os.close(self._stop_event_source) return else: device = self.monitor.poll(timeout=0) if device: self._callback(device) 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_sink is None: return try: # emit a stop event to the thread os.write(self._stop_event_sink, b'\x01') finally: # close the out-of-thread side of the pipe os.close(self._stop_event_sink) self._stop_event_sink = None 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 pyudev-0.16.1/pyudev/_libudev.py0000644000175000001440000002365512006301274017616 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 """ _libudev ======== Wrapper types for libudev. Use ``libudev`` attribute to access libudev functions. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import os import errno from ctypes import (CDLL, Structure, POINTER, get_errno, c_char, c_char_p, c_int, c_ulonglong) from ctypes.util import find_library class udev(Structure): """ Dummy for ``udev`` structure. """ pass udev_p = POINTER(udev) class udev_enumerate(Structure): """ Dummy for ``udev_enumerate`` structure. """ udev_enumerate_p = POINTER(udev_enumerate) class udev_list_entry(Structure): """ Dummy for ``udev_list_entry`` structure. """ udev_list_entry_p = POINTER(udev_list_entry) class udev_device(Structure): """ Dummy for ``udev_device`` structure. """ udev_device_p = POINTER(udev_device) class udev_monitor(Structure): """ Dummy for ``udev_device`` structure. """ udev_monitor_p = POINTER(udev_monitor) dev_t = c_ulonglong SIGNATURES = { # context 'udev': dict( new=([], udev_p), unref=([udev_p], None), ref=([udev_p], udev_p), get_sys_path=([udev_p], c_char_p), get_dev_path=([udev_p], c_char_p), get_run_path=([udev_p], c_char_p), get_log_priority=([udev_p], c_int), set_log_priority=([udev_p, c_int], None)), # enumeration 'udev_enumerate': dict( new=([udev_p], udev_enumerate_p), ref=([udev_enumerate_p], udev_enumerate_p), unref=([udev_enumerate_p], None), add_match_subsystem=([udev_enumerate_p, c_char_p], c_int), add_nomatch_subsystem=([udev_enumerate_p, c_char_p], c_int), add_match_property=([udev_enumerate_p, c_char_p, c_char_p], c_int), add_match_sysattr=([udev_enumerate_p, c_char_p, c_char_p], c_int), add_nomatch_sysattr=([udev_enumerate_p, c_char_p, c_char_p], c_int), add_match_tag=([udev_enumerate_p, c_char_p], c_int), add_match_sysname=([udev_enumerate_p, c_char_p], c_int), add_match_parent=([udev_enumerate_p, udev_device_p], c_int), add_match_is_initialized=([udev_enumerate_p], c_int), scan_devices=([udev_enumerate_p], c_int), get_list_entry=([udev_enumerate_p], udev_list_entry_p)), # list entries 'udev_list_entry': dict( get_next=([udev_list_entry_p], udev_list_entry_p), get_name=([udev_list_entry_p], c_char_p), get_value=([udev_list_entry_p], c_char_p)), # devices 'udev_device': dict( ref=([udev_device_p], udev_device_p), unref=([udev_device_p], None), new_from_syspath=([udev_p, c_char_p], udev_device_p), new_from_subsystem_sysname=([udev_p, c_char_p, c_char_p], udev_device_p), new_from_devnum=([udev_p, c_char, dev_t], udev_device_p), new_from_environment=([udev_p], udev_device_p), get_parent=([udev_device_p], udev_device_p), get_parent_with_subsystem_devtype=([udev_device_p, c_char_p, c_char_p], udev_device_p), get_devpath=([udev_device_p], c_char_p), get_subsystem=([udev_device_p], c_char_p), get_syspath=([udev_device_p], c_char_p), get_sysnum=([udev_device_p], c_char_p), get_sysname=([udev_device_p], c_char_p), get_driver=([udev_device_p], c_char_p), get_devtype=([udev_device_p], c_char_p), get_devnode=([udev_device_p], c_char_p), get_property_value=([udev_device_p, c_char_p], c_char_p), get_sysattr_value=([udev_device_p, c_char_p], c_char_p), get_devnum=([udev_device_p], dev_t), get_action=([udev_device_p], c_char_p), get_seqnum=([udev_device_p], c_ulonglong), get_is_initialized=([udev_device_p], c_int), get_usec_since_initialized=([udev_device_p], c_ulonglong), get_devlinks_list_entry=([udev_device_p], udev_list_entry_p), get_tags_list_entry=([udev_device_p], udev_list_entry_p), get_properties_list_entry=([udev_device_p], udev_list_entry_p), get_sysattr_list_entry=([udev_device_p], udev_list_entry_p), has_tag=([udev_device_p, c_char_p], c_int)), # monitoring 'udev_monitor': dict( ref=([udev_monitor_p], udev_monitor_p), unref=([udev_monitor_p], None), new_from_netlink=([udev_p, c_char_p], udev_monitor_p), enable_receiving=([udev_monitor_p], c_int), set_receive_buffer_size=([udev_monitor_p, c_int], c_int), get_fd=([udev_monitor_p], c_int), receive_device=([udev_monitor_p], udev_device_p), filter_add_match_subsystem_devtype=( [udev_monitor_p, c_char_p, c_char_p], c_int), filter_add_match_tag=([udev_monitor_p, c_char_p], c_int), filter_update=([udev_monitor_p], c_int), filter_remove=([udev_monitor_p], c_int)) } ERRNO_EXCEPTIONS = { errno.ENOMEM: MemoryError, errno.EOVERFLOW: OverflowError, errno.EINVAL: ValueError } def exception_from_errno(errno): """ Create an exception from ``errno``. ``errno`` is an integral error number. Return an exception object appropriate to ``errno``. """ exception = ERRNO_EXCEPTIONS.get(errno) if exception is not None: return exception() else: return EnvironmentError(errno, os.strerror(errno)) def check_negative_errorcode(result, func, *args): """ Error checker for udev 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 errno = -result raise exception_from_errno(errno) else: return result def check_errno(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: errno = get_errno() if errno != 0: raise exception_from_errno(errno) return result def check_errno_on_null_pointer(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. """ if not result: errno = get_errno() if errno != 0: raise exception_from_errno(errno) return result ERROR_CHECKERS = dict( 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_monitor_set_receive_buffer_size=check_errno, # 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, udev_monitor_receive_device=check_errno_on_null_pointer, 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, udev_monitor_filter_remove=check_errno, ) def load_udev_library(): """ Load the ``udev`` library and return a :class:`ctypes.CDLL` object for it. The library has errno handling enabled. Important functions are given proper signatures and return types to support type checking and argument conversion. Raise :exc:`~exceptions.ImportError`, if the udev library was not found. """ udev_library_name = find_library('udev') if not udev_library_name: raise ImportError('No library named udev') libudev = CDLL(udev_library_name, use_errno=True) # context function signature for namespace, members in SIGNATURES.items(): for funcname in members: fullname = '{0}_{1}'.format(namespace, funcname) func = getattr(libudev, fullname, None) if func: argtypes, restype = members[funcname] func.argtypes = argtypes func.restype = restype errorchecker = ERROR_CHECKERS.get(fullname) if errorchecker: func.errcheck = errorchecker return libudev libudev = load_udev_library() pyudev-0.16.1/pyudev/pyside.py0000644000175000001440000000602512006301274017312 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 .. versionadded:: 0.6 """ from __future__ import (print_function, division, unicode_literals, absolute_import) from PySide.QtCore import QSocketNotifier, QObject, Signal from pyudev._util import text_type from pyudev.core import Device from pyudev._qt_base import QUDevMonitorObserverMixin class QUDevMonitorObserver(QObject, QUDevMonitorObserverMixin): """ An observer for device events integrating into the :mod:`PySide` mainloop. This class inherits :class:`~PySide.QtCore.QObject` to turn device events into Qt signals: >>> from pyudev import Context, Monitor >>> from pyudev.pyqt4 import QUDevMonitorObserver >>> context = Context() >>> monitor = Monitor.from_netlink(context) >>> monitor.filter_by(subsystem='input') >>> observer = QUDevMonitorObserver(monitor) >>> def device_connected(device): ... print('{0!r} added'.format(device)) >>> observer.deviceAdded.connect(device_connected) >>> monitor.start() This class is a child of :class:`~PySide.QtCore.QObject`. """ #: emitted upon arbitrary device events deviceEvent = Signal(text_type, Device) #: emitted, if a device was added deviceAdded = Signal(Device) #: emitted, if a device was removed deviceRemoved = Signal(Device) #: emitted, if a device was changed deviceChanged = Signal(Device) #: emitted, if a device was moved deviceMoved = Signal(Device) def __init__(self, monitor, parent=None): """ Observe the given ``monitor`` (a :class:`~pyudev.Monitor`): ``parent`` is the parent :class:`~PySide.QtCore.QObject` of this object. It is passed unchanged to the inherited constructor of :class:`~PySide.QtCore.QObject`. """ QObject.__init__(self, parent) self._setup_notifier(monitor, QSocketNotifier) pyudev-0.16.1/pyudev/_util.py0000644000175000001440000001063112006301274017127 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) import os import sys import stat from pyudev._libudev import libudev if sys.version_info[0] == 2: text_type = unicode else: text_type = str 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, text_type): 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 else: return ensure_byte_string(text_type(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(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) # for the sake of readability _is_char_device = stat.S_ISCHR _is_block_device = stat.S_ISBLK 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 _is_char_device(mode): return 'char' elif _is_block_device(mode): return 'block' else: raise ValueError('not a device file: {0!r}'.format(filename)) pyudev-0.16.1/pyudev/_compat.py0000644000175000001440000000267412006301274017445 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011 Sebastian Wiesner # 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._compat ============== Compatibility for Python versions, that lack certain functions. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) from subprocess import Popen, CalledProcessError, PIPE def check_output(command): """ Compatibility with :func:`subprocess.check_output` from Python 2.7 and upwards. """ proc = Popen(command, stdout=PIPE) output = proc.communicate()[0] if proc.returncode != 0: raise CalledProcessError(proc.returncode, command) return output pyudev-0.16.1/pyudev/__init__.py0000644000175000001440000000324012006301274017550 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) __version__ = '0.16.1' __version_info__ = tuple(map(int, __version__.split('.'))) __all__ = ['Context', 'Device'] from pyudev.device import * from pyudev.core import * from pyudev.monitor import * pyudev-0.16.1/pyudev/_qt_base.py0000644000175000001440000000511312006301274017567 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 support. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) class QUDevMonitorObserverMixin(object): 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) self._action_signal_map = { 'add': self.deviceAdded, 'remove': self.deviceRemoved, 'change': self.deviceChanged, 'move': self.deviceMoved, } @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: self.deviceEvent.emit(device.action, device) signal = self._action_signal_map.get(device.action) if signal is not None: signal.emit(device) pyudev-0.16.1/pyudev/wx.py0000644000175000001440000000736512006301274016463 0ustar swiesnerusers00000000000000# -*- 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:`WxUDevMonitorObserver` 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 .. versionadded:: 0.14 """ from __future__ import (print_function, division, unicode_literals, absolute_import) from wx import EvtHandler, PostEvent from wx.lib.newevent import NewEvent from pyudev.monitor import MonitorObserver DeviceEvent, EVT_DEVICE_EVENT = NewEvent() DeviceAddedEvent, EVT_DEVICE_ADDED = NewEvent() DeviceRemovedEvent, EVT_DEVICE_REMOVED = NewEvent() DeviceChangedEvent, EVT_DEVICE_CHANGED = NewEvent() DeviceMovedEvent, EVT_DEVICE_MOVED = NewEvent() class WxUDevMonitorObserver(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, Device >>> from pyudev.wx import WxUDevMonitorObserver >>> context = Context() >>> monitor = Monitor.from_netlink(context) >>> monitor.filter_by(subsystem='input') >>> observer = WxUDevMonitorObserver(monitor) >>> def device_connected(event): ... print('{0!r} added'.format(event.device)) >>> observer.Bind(EVT_DEVICE_ADDED, device_connected) >>> monitor.start() This class is a child of :class:`wx.EvtHandler`. """ _action_event_map = { 'add': DeviceAddedEvent, 'remove': DeviceRemovedEvent, 'change': DeviceChangedEvent, 'move': DeviceMovedEvent } 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 = MonitorObserver( self.monitor, callback=self._emit_events, 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_events(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)) pyudev-0.16.1/pyudev/glib.py0000644000175000001440000001134512006301274016733 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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.pygtk ============ Glib integration. :class:`GUDevMonitorObserver` integrates device monitoring into the Glib mainloop by turing device events into Glib signals. :mod:`glib` and :mod:`gobject` from PyGObject_ must be available when importing this module. PyGtk is not required. .. _PyGObject: http://www.pygtk.org/ .. moduleauthor:: Sebastian Wiesner .. versionadded:: 0.7 """ from __future__ import (print_function, division, unicode_literals, absolute_import) # thanks to absolute imports, this really imports the glib binding and not this # module again import glib import gobject class GUDevMonitorObserver(gobject.GObject): """ An observer for device events integrating into the :mod:`glib` mainloop. This class inherits :class:`~gobject.GObject` to turn device events into glib signals. >>> from pyudev import Context, Monitor >>> from pyudev.glib import GUDevMonitorObserver >>> context = Context() >>> monitor = Monitor.from_netlink(context) >>> monitor.filter_by(subsystem='input') >>> observer = GUDevMonitorObserver(monitor) >>> def device_connected(observer, device): ... print('{0!r} added'.format(device)) >>> observer.connect('device-added', device_connected) >>> monitor.start() This class is a child of :class:`gobject.GObject`. """ _action_signal_map = { 'add': 'device-added', 'remove': 'device-removed', 'change': 'device-changed', 'move': 'device-moved'} __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_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.GObject.__init__(self) 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: self.event_source = glib.io_add_watch( self.monitor, glib.IO_IN, self._process_udev_event) elif not value and self.event_source is not None: glib.source_remove(self.event_source) def _process_udev_event(self, source, condition): if condition == glib.IO_IN: device = self.monitor.poll(timeout=0) if 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) return True gobject.type_register(GUDevMonitorObserver) pyudev-0.16.1/pyudev/pyqt4.py0000644000175000001440000000604412006301274017077 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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:`QUDevMonitorObserver` integrates device monitoring into the PyQt4_ mainloop by turing 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) from PyQt4.QtCore import QSocketNotifier, QObject, pyqtSignal from pyudev._util import text_type from pyudev.core import Device from pyudev._qt_base import QUDevMonitorObserverMixin class QUDevMonitorObserver(QObject, QUDevMonitorObserverMixin): """ An observer for device events integrating into the :mod:`PyQt4` mainloop. This class inherits :class:`~PyQt4.QtCore.QObject` to turn device events into Qt signals: >>> from pyudev import Context, Monitor >>> from pyudev.pyqt4 import QUDevMonitorObserver >>> context = Context() >>> monitor = Monitor.from_netlink(context) >>> monitor.filter_by(subsystem='input') >>> observer = QUDevMonitorObserver(monitor) >>> def device_connected(device): ... print('{0!r} added'.format(device)) >>> observer.deviceAdded.connect(device_connected) >>> monitor.start() This class is a child of :class:`~PyQt4.QtCore.QObject`. """ #: emitted upon arbitrary device events deviceEvent = pyqtSignal(text_type, Device) #: emitted, if a device was added deviceAdded = pyqtSignal(Device) #: emitted, if a device was removed deviceRemoved = pyqtSignal(Device) #: emitted, if a device was changed deviceChanged = pyqtSignal(Device) #: emitted, if a device was moved deviceMoved = pyqtSignal(Device) def __init__(self, monitor, parent=None): """ Observe the given ``monitor`` (a :class:`~pyudev.Monitor`): ``parent`` is the parent :class:`~PyQt4.QtCore.QObject` of this object. It is passed unchanged to the inherited constructor of :class:`~PyQt4.QtCore.QObject`. """ QObject.__init__(self, parent) self._setup_notifier(monitor, QSocketNotifier) pyudev-0.16.1/pyudev/core.py0000644000175000001440000003336112006301274016750 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011 Sebastian Wiesner # 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) try: from subprocess import check_output except ImportError: from pyudev._compat import check_output from pyudev.device import Device from pyudev._libudev import libudev from pyudev._util import (ensure_unicode_string, ensure_byte_string, udev_list_iterate, property_value_to_bytes) __all__ = ['udev_version', 'Context', 'Enumerator'] 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`` utilitiy, 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()) 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._as_parameter_ = libudev.udev_new() def __del__(self): libudev.udev_unref(self) @property def sys_path(self): """ The ``sysfs`` mount point defaulting to ``/sys'`` as unicode string. """ if hasattr(libudev, 'udev_get_sys_path'): return ensure_unicode_string(libudev.udev_get_sys_path(self)) else: # Fixed path since udev 183 return '/sys' @property def device_path(self): """ The device directory path defaulting to ``/dev`` as unicode string. """ if hasattr(libudev, 'udev_get_dev_path'): return ensure_unicode_string(libudev.udev_get_dev_path(self)) else: # Fixed path since udev 183 return '/dev' @property def run_path(self): """ The run runtime directory path defaulting to ``/run`` as unicode string. .. udevversion:: 167 .. versionadded:: 0.10 """ if hasattr(libudev, 'udev_get_run_path'): return ensure_unicode_string(libudev.udev_get_run_path(self)) else: 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 libudev.udev_get_log_priority(self) @log_priority.setter def log_priority(self, value): 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_ = libudev.udev_enumerate_new(context) def __del__(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 property, value in kwargs.items(): self.match_property(property, 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``. Return the instance again. """ match = (libudev.udev_enumerate_add_match_subsystem if not nomatch else libudev.udev_enumerate_add_nomatch_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 """ libudev.udev_enumerate_add_match_sysname( self, ensure_byte_string(sys_name)) return self def match_property(self, property, value): """ Include all devices, whose ``property`` has the given ``value``. ``property`` 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. """ libudev.udev_enumerate_add_match_property( self, ensure_byte_string(property), 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 = (libudev.udev_enumerate_add_match_sysattr if not nomatch else 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 """ 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 """ 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 """ libudev.udev_enumerate_add_match_parent(self, parent) return self def __iter__(self): """ Iterate over all matching devices. Yield :class:`Device` objects. """ libudev.udev_enumerate_scan_devices(self) entry = libudev.udev_enumerate_get_list_entry(self) for name, _ in udev_list_iterate(entry): yield Device.from_sys_path(self.context, name) pyudev-0.16.1/pyudev/device.py0000644000175000001440000011017412006301274017255 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011, 2012 Sebastian Wiesner # 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) import os from collections import Mapping, Container, Iterable from datetime import timedelta from pyudev._libudev import libudev from pyudev._util import (ensure_byte_string, ensure_unicode_string, udev_list_iterate, string_to_bool, get_device_type) __all__ = ['Device', 'Attributes', 'Tags', 'DeviceNotFoundError', 'DeviceNotFoundAtPathError', 'DeviceNotFoundByNameError', 'DeviceNotFoundByNumberError', 'DeviceNotFoundInEnvironmentError'] class DeviceNotFoundError(LookupError): """ An exception indicating that no :class:`Device` was found. .. versionchanged:: 0.5 Rename from ``NoSuchDeviceError`` to its current name. """ 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 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, type, number): DeviceNotFoundError.__init__(self, type, 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 Device(Mapping): """ A single device with attached attributes and properties. This class subclasses the ``Mapping`` ABC, providing a read-only dictionary mapping property names to the corresponding values. Therefore all well-known dicitionary methods and operators (e.g. ``.keys()``, ``.items()``, ``in``) are available to access device properties. Aside of the 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:: Do **never** use object identity (``is`` operator) to compare :class:`Device` objects. :mod:`pyudev` may create multiple :class:`Device` objects for the same device. Instead simply 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): """ 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() >>> Device.from_path(context, '/devices/platform') Device(u'/sys/devices/platform') >>> Device.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.4 """ 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() >>> Device.from_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``. .. 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`. """ device = libudev.udev_device_new_from_syspath( context, ensure_byte_string(sys_path)) if not device: raise DeviceNotFoundAtPathError(sys_path) return cls(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 = Device.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 == Device.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.5 """ device = 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 cls(context, device) @classmethod def from_device_number(cls, context, type, 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 = Device.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. Raise :exc:`~exceptions.ValueError`, if ``type`` is any other string than ``'char'`` or ``'block'``. .. versionadded:: 0.11 """ if type not in ('char', 'block'): raise ValueError('Invalid type: {0!r}. Must be one of "char" ' 'or "block".'.format(type)) device = libudev.udev_device_new_from_devnum( context, ensure_byte_string(type[0]), number) if not device: raise DeviceNotFoundByNumberError(type, number) return cls(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 = Device.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 = Device.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:`~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 """ device_type = get_device_type(filename) device_number = os.stat(filename).st_rdev return cls.from_device_number(context, device_type, device_number) @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.6 """ device = libudev.udev_device_new_from_environment(context) if not device: raise DeviceNotFoundInEnvironmentError() return cls(context, device) def __init__(self, context, _device): self.context = context self._as_parameter_ = _device def __del__(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 = 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, 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: 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 = 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, 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. """ import warnings warnings.warn('Will be removed in 1.0. Use Device.ancestors instead.', DeprecationWarning) 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(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(libudev.udev_device_get_devpath(self)) @property def subsystem(self): """ Name of the subsystem this device is part of as unicode string. """ return ensure_unicode_string(libudev.udev_device_get_subsystem(self)) @property def sys_name(self): """ Device file name inside ``sysfs`` as unicode string. """ return ensure_unicode_string(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 = Device.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 = libudev.udev_device_get_sysnum(self) if number is not None: return ensure_unicode_string(number) @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 = libudev.udev_device_get_devtype(self) if device_type is not 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 = libudev.udev_device_get_driver(self) if driver: return ensure_unicode_string(driver) @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 = libudev.udev_device_get_devnode(self) if node: return ensure_unicode_string(node) @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 = Device.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 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(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 = 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:`Device.from_device_file()`. Hence do *not* rely on ``Device.from_device_file(context, link).device_path == device.device_path`` from any ``link`` in ``device.device_links``. """ devlinks = libudev.udev_device_get_devlinks_list_entry(self) for name, _ in udev_list_iterate(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 = libudev.udev_device_get_action(self) if action: return ensure_unicode_string(action) @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 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 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. .. 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 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, Device >>> 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. """ properties = libudev.udev_device_get_properties_list_entry(self) for name, _ in udev_list_iterate(properties): yield ensure_unicode_string(name) def __len__(self): """ Return the amount of properties defined for this device as integer. """ properties = libudev.udev_device_get_properties_list_entry(self) i = 0 for i, _ in enumerate(udev_list_iterate(properties), start=1): pass return i def __getitem__(self, property): """ Get the given ``property`` from this device. ``property`` 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 = libudev.udev_device_get_property_value( self, ensure_byte_string(property)) if value is None: raise KeyError(property) return ensure_unicode_string(value) def asint(self, property): """ Get the given ``property`` from this device as integer. ``property`` 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[property]) def asbool(self, property): """ 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. ``property`` 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[property]) def __hash__(self): return hash(self.device_path) def __eq__(self, other): if isinstance(other, Device): return self.device_path == other.device_path else: return self.device_path == other def __ne__(self, other): if isinstance(other, Device): return self.device_path != other.device_path else: 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 Tags(Iterable, Container): """ A iterable over :class:`Device` tags. Subclasses the ``Container`` and the ``Iterable`` ABC. """ def __init__(self, device): self.device = device if hasattr(libudev, 'udev_device_has_tag'): def _has_tag(self, tag): return bool(libudev.udev_device_has_tag( self.device, ensure_byte_string(tag))) else: def _has_tag(self, tag): return any(t == tag for t in self) 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 = libudev.udev_device_get_tags_list_entry(self.device) for tag, _ in udev_list_iterate(tags): yield ensure_unicode_string(tag) def _is_attribute_file(filepath): """ Check, if ``filepath`` points to a valid udev attribute filename. Implementation is stolen from udev source code, ``print_all_attributes`` in ``udev/udevadm-info.c``. It excludes hidden files (starting with a dot), the special files ``dev`` and ``uevent`` and links. Return ``True``, if ``filepath`` refers to an attribute, ``False`` otherwise. """ filename = os.path.basename(filepath) return not (filename.startswith('.') or filename in ('dev', 'uevent') or os.path.islink(filepath)) class Attributes(Mapping): """ A mapping which holds udev attributes for :class:`Device` objects. This class subclasses the ``Mapping`` ABC, providing a read-only dictionary mapping attribute names to the corresponding values. Therefore all well-known dicitionary methods and operators (e.g. ``.keys()``, ``.items()``, ``in``) are available to access device attributes. .. versionadded:: 0.5 """ def __init__(self, device): self.device = device if hasattr(libudev, 'udev_device_get_sysattr_list_entry'): @property def _attributes(self): attrs = libudev.udev_device_get_sysattr_list_entry(self.device) for attribute, _ in udev_list_iterate(attrs): yield ensure_unicode_string(attribute) else: @property def _attributes(self): sys_path = self.device.sys_path return (fn for fn in os.listdir(sys_path) if _is_attribute_file(os.path.join(sys_path, fn)) and fn in self) def __len__(self): """ Return the amount of attributes defined. """ i = 0 for i, _ in enumerate(self._attributes, start=1): pass return i def __iter__(self): """ Iterate over all attributes defined. Yield each attribute name as unicode string. """ return self._attributes def __contains__(self, attribute): value = libudev.udev_device_get_sysattr_value( self.device, ensure_byte_string(attribute)) return value is not None def __getitem__(self, attribute): """ Get the given system ``attribute`` for the device. ``attribute`` is a unicode or byte string containing the name of the system attribute. Return the attribute value as byte string, or raise a :exc:`~exceptions.KeyError`, if the given attribute is not defined for this device. """ value = libudev.udev_device_get_sysattr_value( self.device, ensure_byte_string(attribute)) if value is None: raise KeyError(attribute) return value def asstring(self, attribute): """ Get the given ``atribute`` for the device as unicode string. Depending on the content of the attribute, this may or may not work. Be prepared to catch :exc:`~exceptions.UnicodeDecodeError`. ``attribute`` is a unicode or byte string containing the name of the attribute. Return the attribute value as byte string. Raise a :exc:`~exceptions.KeyError`, if the given attribute is not defined for this device, or :exc:`~exceptions.UnicodeDecodeError`, if the content of the attribute cannot be decoded into a unicode string. """ return ensure_unicode_string(self[attribute]) def asint(self, attribute): """ Get the given ``attribute`` as integer. ``attribute`` is a unicode or byte string containing the name of the attribute. Return the attribute value as integer. Raise a :exc:`~exceptions.KeyError`, if the given attribute is not defined for this device, or a :exc:`~exceptions.ValueError`, if the attribute value cannot be converted to an integer. """ return int(self.asstring(attribute)) def asbool(self, attribute): """ Get the given ``attribute`` from this device as boolean. 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. ``attribute`` is a unicode or byte string containing the name of the attribute. Return ``True``, if the attribute value is ``'1'`` and ``False``, if the attribute value is ``'0'``. Any other value raises a :exc:`~exceptions.ValueError`. Raise a :exc:`~exceptions.KeyError`, if the given attribute is not defined for this device. """ return string_to_bool(self.asstring(attribute)) pyudev-0.16.1/requirements.txt0000644000175000001440000000017512006301274017413 0ustar swiesnerusers00000000000000# unit test requirements pytest>=2.2 mock>=1.0b1 # documentation requirements sphinx>=1.0.7 sphinxcontrib-issuetracker>=0.9 pyudev-0.16.1/doc/0000755000175000001440000000000012006442367014702 5ustar swiesnerusers00000000000000pyudev-0.16.1/doc/changes.rst0000644000175000001440000000006112006301274017030 0ustar swiesnerusers00000000000000Changelog ********* .. include:: ../CHANGES.rst pyudev-0.16.1/doc/_templates/0000755000175000001440000000000012006442367017037 5ustar swiesnerusers00000000000000pyudev-0.16.1/doc/_templates/info.html0000644000175000001440000000172312006301274020652 0ustar swiesnerusers00000000000000{% macro link(title, link, internal=false) -%} {{ title }} {%- endmacro %}

pyudev {{release}}

Install

  • pip install pyudev

Links

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

Support

  • {{link('mailing list', 'mailto:pyudev@librelist.com')}} ({{link('archives', 'http://librelist.com/browser/pyudev')}})
  • {{link('issue tracker', 'https://github.com/lunaryorn/pyudev/issues')}}
pyudev-0.16.1/doc/api/0000755000175000001440000000000012006442367015453 5ustar swiesnerusers00000000000000pyudev-0.16.1/doc/api/pyudev.pyqt4.rst0000644000175000001440000000271412006301274020574 0ustar swiesnerusers00000000000000:mod:`pyudev.pyqt4` – PyQt4_ integration ======================================== .. automodule:: pyudev.pyqt4 :platform: Linux :synopsis: PyQt4 integration .. 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 defines 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. .. _PyQt4: http://riverbankcomputing.co.uk/software/pyqt/intro pyudev-0.16.1/doc/api/pyudev.rst0000644000175000001440000001175212006301274017516 0ustar swiesnerusers00000000000000:mod:`pyudev` - libudev binding =============================== .. automodule:: pyudev :platform: Linux :synopsis: libudev bindings .. autosummary:: :nosignatures: Context Device 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:`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 pyudev-0.16.1/doc/api/pyudev.glib.rst0000644000175000001440000000313512006301274020426 0ustar swiesnerusers00000000000000:mod:`pyudev.glib` – Glib/Gtk 2 integration =========================================== .. automodule:: pyudev.glib :platform: Linux :synopsis: Glib integration .. 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 defines 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. pyudev-0.16.1/doc/api/index.rst0000644000175000001440000000041412006301274017302 0ustar swiesnerusers00000000000000API 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.pyside pyudev.glib pyudev.wx pyudev-0.16.1/doc/api/pyudev.wx.rst0000644000175000001440000000354712006301274020156 0ustar swiesnerusers00000000000000:mod:`pyudev.wx` – wxPython_ integration ===================================================== .. automodule:: pyudev.wx :platform: Linux :synopsis: wxWidgets integration .. autoclass:: WxUDevMonitorObserver .. attribute:: monitor The :class:`~pyudev.Monitor` observed by this object. .. autoattribute:: enabled .. rubric:: Event constants :class:`WxUDevMonitorObserver` exposes the following events: .. data:: EVT_DEVICE_EVENT Emitted upon any device event. Receivers get a :class:`DeviceEvent` object as argument. .. 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. .. 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. .. 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. .. data:: EVT_DEVICE_MOVED Emitted if a :class:`~pyudev.Device` was renamed, moved or re-parented. Receivers get a :class:`DeviceMovedEvent` object as argument. .. rubric:: Event objects .. class:: DeviceEvent Argument object for :data:`EVT_DEVICE_EVENT`. .. attribute:: action A unicode string containing the action name. .. attribute:: device The :class:`~pyudev.Device` object that caused this event. .. 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`. .. attribute:: device The :class:`~pyudev.Device` object that caused this event. pyudev-0.16.1/doc/api/pyudev.pyside.rst0000644000175000001440000000266512006301274021015 0ustar swiesnerusers00000000000000:mod:`pyudev.pyside` – PySide_ integration ========================================== .. automodule:: pyudev.pyside :platform: Linux :synopsis: PySide integration .. 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 defines 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. .. _PySide: http://www.pyside.org pyudev-0.16.1/doc/guide.rst0000644000175000001440000003311512006301274016523 0ustar swiesnerusers00000000000000User guide ========== .. currentmodule:: pyudev This guide gives an introduction in how to use pyudev for common operations like device enumeration or monitoring: .. contents:: A detailled reference is provided in the :doc:`API documentation `. 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:`Device.from_path()`), by from a subsystem and device name (:meth:`Device.from_name()`) or from a device file (:meth:`Device.from_device_file()`). The following code gets the :class:`Device` object for the first hard disc in thrww different ways: >>> pyudev.Device.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.Device.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.Device.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.Device.from_path(context, '/sys/block/sda') == pyudev.Device.from_name(context, 'block', 'sda') True Whereas :class:`Device` objects referring to different devices are unequal: >>> pyudev.Device.from_name(context, 'block', 'sda') == pyudev.Device.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 as 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 all other properties, :class:`Device` provides a dictionary-like interface to directly access the device properties. You'll get the same information has with the generic properties: >>> for device in context.list_devices(subsystem='block'): ... print('{0} ({1})'.format(device['DEVNAME'], device['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.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(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(action, device): ... if 'ID_FS_TYPE' in device: ... with open('filesystems.log', 'a+') as stream: ... print('{0} - {1}'.format(action, device.get('ID_FS_LABEL')), file=stream) ... >>> observer = pyudev.MonitorObserver(monitor, log_event) >>> observer.start() The ``observer`` gets an event handler (``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_ 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() .. _pypi: https://pypi.python.org/pypi/pyudev .. _libudev: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ .. _Qt: http://qt-project.org/ .. _PyQt4: http://riverbankcomputing.co.uk/software/pyqt/intro .. _PySide: http://www.pyside.org .. _PyGtk: http://www.pygtk.org/ .. _wxWidgets: http://wxwidgets.org .. _wxPython: http://www.wxpython.org pyudev-0.16.1/doc/tests/0000755000175000001440000000000012006442367016044 5ustar swiesnerusers00000000000000pyudev-0.16.1/doc/tests/running.rst0000644000175000001440000000542012006301274020246 0ustar swiesnerusers00000000000000Test running ============ Automatic execution using tox_ ------------------------------ The recommended way to run all tests is to use tox_. This tool automatically creates virtual environments with virtualenv_ with all requirements installed and runs the tests. Assuming that tox_ is installed, the following command will run the pyudev tests against Python 2.7, Python 3.2 and PyPy:: tox -e py27,py32,pypy You can pass arbitrary :program:`py.test` arguments after two dashes ``--``:: tox -e py27,py32,pypy -- --enable-privileged Manual execution ---------------- You can also run tests manually with :program:`py.test`. It is recommended to setup a separate virtual environment for this purpose:: $ virtualenv pyudv $ . pyudev/bin/activate.sh Then use the provided :file:`requirements.txt` file to install the necessary modules into this virtual environment by running the following command from the root of the pyudev source tree:: $ pip install -r requirements.txt Notes ----- 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. Privileged tests ~~~~~~~~~~~~~~~~ Some tests need to execute privileged operations like loading or unloading of kernel modules to trigger real udev events. These tests are disabled by default. Refer to :mod:`~tests.plugins.privileged` for more information on how to enable these tests and configure them properly. Native bindings ~~~~~~~~~~~~~~~ Some tests require native bindings to other libraries. These bindings cannot be installed by means of a ``requirements.txt`` file, but need to be build instead. Since building these bindings is cumbersome and difficult, especially inside virtualenvs, the ``build_bindings.py`` is provided to automate these builds. ``tox`` is configured to execute this script before running the tests, so that tox tests will always have these bindings available. For custom virtualenvs however you need to perform this step manually after virtualenv creation:: python build_bindings.py .. warning:: By default, builds are done under ``/tmp``, so make sure that there is enough space available on this filesystem, especially if it is located on ``tmpfs``. Use the ``--download-directory`` and ``--build-directory`` options to change the corresponding directories if needed. See ``python build_bindings.py --help`` for more information. .. _virtualenv: http://www.virtualenv.org/en/latest/index.html .. _tox: http://tox.testrun.org/latest/ pyudev-0.16.1/doc/tests/index.rst0000644000175000001440000000070512006301274017676 0ustar swiesnerusers00000000000000Testsuite 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/ pyudev-0.16.1/doc/tests/plugins.rst0000644000175000001440000001415612006301274020255 0ustar swiesnerusers00000000000000:mod:`plugins` – Testsuite plugins ================================== .. automodule:: plugins The following plugins are provided and enabled: .. autosummary:: udev_database privileged fake_monitor mock_libudev libudev The main plugin is :mod:`~plugins.udev_database` that extracts the real udev database using the ``udevadm`` utility and provides tests with a sample of this database. It also supports to restrict tests to certain udev versions. The other plugins only provide support for specific test cases by tuning some hooks or adding some additional funcargs. :mod:`~plugins.udev_database` – pytest_ plugin to access the udev device database --------------------------------------------------------------------------------- .. automodule:: plugins.udev_database .. autoclass:: DeviceDatabase() :members: .. autoclass:: DeviceData() :members: .. attribute:: device_path The path of the device without the ``sysfs`` mountpoint. Test markers ~~~~~~~~~~~~ .. function:: pytest.mark.udev_version(version_spec) Specify the udev version requirement for a test:: @pytest.mark.udev_version('>= 180') def test_foo(): assert True ``test_foo`` will only be run, if the udev version is greater or equal than 180. Otherwise the test is skipped. ``version_spec`` is a string specifying the udev version requirement. If the requirement is not met, the test is skipped. Configuration values ~~~~~~~~~~~~~~~~~~~~ The plugin attaches the following attributes to :data:`pytest.config`: .. attribute:: pytest.config.udev_version The udev version as integer. .. attribute:: pytest.config.udev_database The whole udev device database as :class:`DeviceDatabase` object. .. attribute:: pytest.config.udev_device_sample A list of devices to use for tests as list of :class:`DeviceData` objects, an excerpt of :attr:`pytest.config.udev_database`. Funcargs ~~~~~~~~ The plugin provides the following :ref:`funcargs `: .. autofunction:: pytest_funcarg__udev_database Command line options ~~~~~~~~~~~~~~~~~~~~ The plugin adds the following command line options to :program:`py.test`: .. program:: py.test .. option:: --all-devices Run device tests against *all* devices in the database. By default, only a random sample of devices are being tested against. .. warning:: Tests may take a very long time with this option. .. option:: --device DEVICE Run device tests against a specific device only. ``DEVICE`` is the device path *without* the sysfs mountpoint. .. option:: --device-sample-size N The size of the random sample. Defaults to 10. :mod:`~plugins.privileged` – Privileged operations -------------------------------------------------- .. automodule:: plugins.privileged Command line options ~~~~~~~~~~~~~~~~~~~~ The plugin adds the following command line options to :program:`py.test`: .. program:: py.test .. option:: --enable-privileged Enable privileged tests. You'll need to have :program:`sudo` configured correctly in order to run tests with this option. Configuration ~~~~~~~~~~~~~ In order to execute these tests without failure, you need to configure :program:`sudo` to allow the user that executes the test to run the following commands: - ``modprobe dummy`` - ``modprobe -r dummy`` To do so, create a file ``/etc/sudoers.d/20pyudev-tests`` with the following content:: me ALL = (root) NOPASSWD: /sbin/modprobe dummy, /sbin/modprobe -r dummy Replace ``me`` with your actual user name. ``NOPASSWD:`` tells :program:`sudo` not to ask for a password when executing these commands. This is simply for the sake of convenience and to allow unattended test execution. Remove this word if you want to be asked for a password. Make sure to change the owner and group to ``root:root`` and the permissions of this file to ``440`` afterwards, other :program:`sudo` will refuse to load the file. Also check the file with :program:`visudo` to prevent syntactic errors:: $ chown root:root /etc/sudoers.d/20pyudev-tests $ chmod 440 /etc/sudoers.d/20pyudev-tests $ visudo -c -f /etc/sudoers.d/20pyudev-tests :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 `: .. autofunction:: pytest_funcarg__fake_monitor :mod:`~plugins.mock_libudev` – Mock calls to libudev ---------------------------------------------------- .. automodule:: plugins.mock_libudev .. autofunction:: calls_to_libudev(function_calls) .. autofunction:: libudev_list(function, items) :mod:`~plugins.libudev` – Parse ``libudev.h`` --------------------------------------------- .. automodule:: plugins.libudev Configuration values ~~~~~~~~~~~~~~~~~~~~ This plugin attaches the following attribute to :data:`pytest.config`: .. attribute:: libudev_functions All libudev functions as list of :class:`Function` objects. Funcargs ~~~~~~~~ This plugin provides the the following :ref:`funcarg `: .. autofunction:: pytest_funcarg__libudev_function Types ~~~~~ .. autoclass:: GCCXMLParser :members: .. autoclass:: Unit :members: .. rubric:: Symbol classes .. class:: Function A function. .. attribute:: name The function name as string .. attribute:: arguments A tuple providing with the argument types of this function .. attribute:: return_type The return type of this function .. class:: Struct A structure. .. attribute:: name The struct name as string .. class:: FundamentalType A fundamental type. .. attribute:: name The type name as string .. class:: CvQualifiedType A constant-qualified type. .. attribute:: type The underlying type .. class:: PointerType A pointer type. .. attribute:: type The underyling type .. _pytest: http://pytest.org pyudev-0.16.1/doc/index.rst0000644000175000001440000000543712006301274016543 0ustar swiesnerusers00000000000000pyudev -- pure Python libudev_ binding ====================================== pyudev |release| (:doc:`changes`, :doc:`installation `) pyudev is a :doc:`LGPL licensed `, pure Python_ 2/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.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 ask questions about usage and development of pyudev to the mailing list at pyudev@librelist.com hosted by librelist.com_. To subscribe to this list, send a mail to pyudev@librelist.com and reply to the confirmation email. To unsubscribe again, write to pyudev-unsubscribe@librelist.com and reply to the configuration mail. Past discussions are available in the `list archives`_. Please report issues and bugs 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 Other reading ------------- .. toctree:: :maxdepth: 1 changes licencing .. _Python: http://www.python.org/ .. _libudev: http://www.freedesktop.org/software/systemd/libudev/ .. _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 pyudev-0.16.1/doc/licencing.rst0000644000175000001440000000006412006301274017356 0ustar swiesnerusers00000000000000Licencing ========= .. literalinclude:: ../COPYING pyudev-0.16.1/doc/install.rst0000644000175000001440000000245712006301274017101 0ustar swiesnerusers00000000000000Installation ============ Python versions and implementations ----------------------------------- pyudev supports CPython from 2.6 up to the latest Python 3 release, and PyPy 1.5. Jython may work, too, but is not tested. Generally any Python implementation compatible with CPython 2.6 should work. 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 `_:: 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/ pyudev-0.16.1/doc/conf.py0000644000175000001440000001056312006301274016175 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) 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('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', 'sphinxcontrib.issuetracker'] master_doc = 'index' exclude_patterns = ['_build/*'] source_suffix = '.rst' project = u'pyudev' copyright = u'2010, 2011 Sebastian Wiesner' version = '.'.join(pyudev.__version__.split('.')[:2]) release = pyudev.__version__ templates_path = ['_templates'] html_theme = 'default' 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), 'pyside': ('http://www.pyside.org/docs/pyside/', None)} issuetracker = 'github' issuetracker_project = 'lunaryorn/pyudev' 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(b'autodoc-process-docstring', cut_lines(2, what=['module'])) app.add_directive('udevversion', UDevVersion) pyudev-0.16.1/doc/contribute.rst0000644000175000001440000000274612006301274017612 0ustar swiesnerusers00000000000000Contribute ========== 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 `_. - Understand the `branching model `_. - Use a topic branch based on the ``develop`` branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - 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 `). - 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 pyudev-0.16.1/tests/0000755000175000001440000000000012006442367015277 5ustar swiesnerusers00000000000000pyudev-0.16.1/tests/test_libudev.py0000644000175000001440000001111612006301274020331 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import re import ctypes import pytest from pyudev import _libudev as binding libudev = binding.libudev WRAPPER_BLACKLIST_PATTERNS = [ # vararg functions not supported by ctypes 'udev_set_log_fn', # superfluous in Python, because arbitrary attributes can be attached to # objects anyways 'udev_set_userdata', 'udev_get_userdata', # superfluous, because context is already available in .context 'udev_enumerate_get_udev', 'udev_monitor_get_udev', 'udev_device_get_udev', # superfluous, because Python provides already tools to filter lists 'udev_list_entry_get_by_name', # superfluous because of ".encode('string-escape')" 'udev_util_encode_string', # deprecated and removed in recent udev versions 'udev_monitor_new_from_socket', # all queue functions (queue interface is not wrapped) re.compile('^udev_queue_.*'), # XXX: I've no clue what these functions actually do 'udev_enumerate_add_syspath', 'udev_enumerate_scan_subsystems', ] def _is_blacklisted(function): for pattern in WRAPPER_BLACKLIST_PATTERNS: if pytest.is_unicode_string(pattern): if function.name == pattern: return True else: if pattern.match(function.name): return True else: return False FUNDAMENTAL_TYPES = { 'int': ctypes.c_int, 'char': ctypes.c_char, 'long long unsigned int': ctypes.c_ulonglong, 'long unsigned int': ctypes.c_ulong, 'void': None, } def _pointer_to_ctypes(pointer): underlying_type = _to_ctypes(pointer.type) if underlying_type is ctypes.c_char: return ctypes.c_char_p else: return ctypes.POINTER(underlying_type) TYPE_CONVERTER = { 'FundamentalType': lambda t: FUNDAMENTAL_TYPES[t.name], 'Struct': lambda s: getattr(binding, s.name), 'PointerType': _pointer_to_ctypes, # const qualifiers are ignored in ctypes 'CvQualifiedType': lambda t: _to_ctypes(t.type), # propagate type defs 'Typedef': lambda t: _to_ctypes(t.type), } def _to_ctypes(libudev_type): return TYPE_CONVERTER[libudev_type.__class__.__name__](libudev_type) class LibudevFunction(object): def __init__(self, declaration): self.declaration = declaration @property def name(self): return self.declaration.name @property def wrapper(self): return getattr(libudev, self.name) @property def argument_types(self): return [_to_ctypes(a) for a in self.declaration.arguments] @property def return_type(self): return _to_ctypes(self.declaration.return_type) def pytest_funcarg__libudev_function(request): """ Override ``libudev_function`` to skip tests for blacklisted functions. """ function = request.getfuncargvalue('libudev_function') if _is_blacklisted(function): pytest.skip('{0} is not wrapped'.format(function.name)) return LibudevFunction(function) def test_arguments(libudev_function): assert libudev_function.wrapper.argtypes == libudev_function.argument_types def test_return_type(libudev_function): # Ignore the return type of *_unref() functions. The return value of these # functions is unused in pyudev, so it doesn't need to be wrapped. restype = (libudev_function.return_type if not libudev_function.name.endswith('_unref') else None) assert libudev_function.wrapper.restype == restype def test_error_checker(libudev_function): name = libudev_function.name if name in binding.ERROR_CHECKERS: assert libudev_function.wrapper.errcheck == \ binding.ERROR_CHECKERS[name] else: pytest.skip('{0} has no error checker'.format(name)) pyudev-0.16.1/tests/test_core.py0000644000175000001440000000613312006301274017632 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import random import syslog import pytest from mock import sentinel from pyudev import udev_version from pyudev._libudev import libudev 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 pytest.is_unicode_string(context.sys_path) assert context.sys_path == '/sys' def test_device_path(self, context): assert pytest.is_unicode_string(context.device_path) assert context.device_path == '/dev' @pytest.mark.udev_version('>= 167') def test_run_path(self, context): assert pytest.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): calls = {'udev_get_log_priority': [(context,)]} with pytest.calls_to_libudev(calls): libudev.udev_get_log_priority.return_value = sentinel.log_priority assert context.log_priority is sentinel.log_priority def test_log_priority_set_mock(self, context): calls = {'udev_set_log_priority': [(context, sentinel.log_priority)]} with pytest.calls_to_libudev(calls): context.log_priority = 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 pyudev-0.16.1/tests/conftest.py0000644000175000001440000000434512006301274017473 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import os import sys import pyudev pytest_plugins = [ str('plugins.udev_database'), str('plugins.fake_monitor'), str('plugins.privileged'), str('plugins.mock_libudev'), str('plugins.libudev'), ] def assert_env_error(error, errno, filename=None): __tracebackhide__ = True # work around an apparent limitation in pytest.raises, which gives use # tuple representations of exceptions instead of exception objects. See # pyudev issue #43 if isinstance(error, tuple): error = OSError(*error) assert error.errno == errno assert error.strerror == os.strerror(errno) assert error.filename == filename 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. """ if sys.version_info[0] >= 3: unicode_type = str else: unicode_type = unicode return isinstance(value, unicode_type) def pytest_namespace(): return dict((func.__name__, func) for func in (is_unicode_string, assert_env_error)) def pytest_funcarg__context(request): """ Return a useable :class:`pyudev.Context` object. The context is cached with session scope. """ return request.cached_setup(setup=pyudev.Context, scope='session') pyudev-0.16.1/tests/test_util.py0000644000175000001440000001071112006301274017654 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import sys import errno import pytest from pyudev import _util @pytest.mark.conversion 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 @pytest.mark.conversion def test_ensure_byte_string_none(): with pytest.raises(AttributeError): _util.ensure_byte_string(None) @pytest.mark.conversion def test_ensure_unicode_string(): assert pytest.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 @pytest.mark.conversion def test_ensure_unicode_string_none(): with pytest.raises(AttributeError): _util.ensure_unicode_string(None) @pytest.mark.conversion 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 @pytest.mark.conversion 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) @pytest.mark.conversion 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) @pytest.mark.conversion def test_string_to_bool_true(): assert isinstance(_util.string_to_bool('1'), bool) assert _util.string_to_bool('1') @pytest.mark.conversion def test_string_to_bool_false(): assert isinstance(_util.string_to_bool('0'), bool) assert not _util.string_to_bool('0') @pytest.mark.conversion 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(None)) def test_udev_list_iterate_mock(): from pyudev._libudev import libudev items = [('spam', 'eggs'), ('foo', 'bar')] with pytest.libudev_list('udev_enumerate_get_list_entry', items): udev_list = libudev.udev_enumerate_get_list_entry() assert list(_util.udev_list_iterate(udev_list)) == [ ('spam', 'eggs'), ('foo', 'bar')] def raise_valueerror(): raise ValueError('from function') def test_get_device_type_character_device(): assert _util.get_device_type('/dev/console') == 'char' def test_get_device_type_block_device(): assert _util.get_device_type('/dev/sda') == '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): filename = tmpdir.join('test') assert not tmpdir.check(file=True) with pytest.raises(EnvironmentError) as excinfo: _util.get_device_type(str(filename)) pytest.assert_env_error(excinfo.value, errno.ENOENT, str(filename)) pyudev-0.16.1/tests/test_monitor.py0000644000175000001440000003054512006301274020375 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import errno from datetime import datetime, timedelta from contextlib import contextmanager from select import select import pytest from mock import Mock, sentinel, patch from pyudev import Monitor, MonitorObserver, Device from pyudev._libudev import libudev # 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. def pytest_funcarg__monitor(request): return Monitor.from_netlink(request.getfuncargvalue('context')) def pytest_funcarg__fake_monitor_device(request): context = request.getfuncargvalue('context') return Device.from_path(context, '/devices/platform') @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): calls = {'udev_monitor_new_from_netlink': [(context, b'udev'), (context, b'udev')]} with pytest.calls_to_libudev(calls): libudev.udev_monitor_new_from_netlink.return_value = sentinel.monitor monitor = Monitor.from_netlink(context) assert monitor._as_parameter_ is sentinel.monitor assert not monitor.started monitor = Monitor.from_netlink(context, 'udev') assert monitor._as_parameter_ is sentinel.monitor assert not monitor.started 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): calls = {'udev_monitor_new_from_netlink': [(context, b'kernel')]} with pytest.calls_to_libudev(calls): libudev.udev_monitor_new_from_netlink.return_value = sentinel.monitor monitor = Monitor.from_netlink(context, 'kernel') assert monitor._as_parameter_ is sentinel.monitor assert not monitor.started 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): calls = {'udev_monitor_get_fd': [(monitor,)]} with pytest.calls_to_libudev(calls): libudev.udev_monitor_get_fd.return_value = sentinel.fileno assert monitor.fileno() is sentinel.fileno 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): calls = {'udev_monitor_filter_add_match_subsystem_devtype': [(monitor, b'input', None)], 'udev_monitor_filter_update': [(monitor,)]} with pytest.calls_to_libudev(calls): monitor.filter_by('input') 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): calls = {'udev_monitor_filter_add_match_subsystem_devtype': [(monitor, b'input', b'usb_interface')], 'udev_monitor_filter_update': [(monitor,)]} with pytest.calls_to_libudev(calls): monitor.filter_by('input', 'usb_interface') @pytest.mark.udev_version('>= 154') def test_filter_by_tag(self, monitor): monitor.filter_by_tag('spam') @pytest.mark.udev_version('>= 154') def test_filter_by_tag_mock(self, monitor): calls = {'udev_monitor_filter_add_match_tag': [(monitor, b'eggs')], 'udev_monitor_filter_update': [(monitor,)]} with pytest.calls_to_libudev(calls): monitor.filter_by_tag('eggs') def test_remove_filter(self, monitor): """ The underlying ``udev_monitor_filter_remove()`` is apparently broken. It always causes ``EINVAL`` from ``setsockopt()``. """ with pytest.raises(ValueError): monitor.remove_filter() def test_remove_filter_mock(self, monitor): calls = {'udev_monitor_filter_remove': [(monitor,)], 'udev_monitor_filter_update': [(monitor,)]} with pytest.calls_to_libudev(calls): monitor.remove_filter() 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): calls = {'udev_monitor_enable_receiving': [(monitor,)]} with pytest.calls_to_libudev(calls): assert not monitor.started monitor.start() assert monitor.started def test_enable_receiving(self, monitor): """ Test that enable_receiving() is deprecated and calls out to start(). """ with patch.object(monitor, 'start') as start: pytest.deprecated_call(monitor.enable_receiving) assert start.called def test_set_receive_buffer_size_mock(self, monitor): calls = {'udev_monitor_set_receive_buffer_size': [(monitor, 1000)]} with pytest.calls_to_libudev(calls): monitor.set_receive_buffer_size(1000) def test_set_receive_buffer_size_privilege_error(self, monitor): with pytest.raises(EnvironmentError) as exc_info: monitor.set_receive_buffer_size(1000) pytest.assert_env_error(exc_info.value, errno.EPERM) 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) @pytest.mark.privileged def test_poll(self, monitor): # forcibly unload the dummy module to avoid hangs pytest.unload_dummy() monitor.filter_by('net') monitor.start() # load the dummy device to trigger an add event pytest.load_dummy() select([monitor], [], []) device = monitor.poll() assert device.action == 'add' assert device.sequence_number > 0 assert device.subsystem == 'net' assert device.device_path == '/devices/virtual/net/dummy0' # and unload again pytest.unload_dummy() device = monitor.poll() assert device.action == 'remove' assert device.sequence_number > 0 assert device.subsystem == 'net' assert device.device_path == '/devices/virtual/net/dummy0' def test_receive_device(self, monitor): """ Test that Monitor.receive_device is deprecated and calls out to _receive_device(), which in turn is tested by test_poll. """ with patch.object(monitor, '_receive_device') as receive_device: device = Mock(name='device') device.action = 'spam' receive_device.return_value = device event = pytest.deprecated_call(monitor.receive_device) assert event[0] == 'spam' assert event[1] is device @pytest.mark.privileged def test_iter(self, monitor): pytest.unload_dummy() monitor.filter_by('net') monitor.start() pytest.load_dummy() iterator = iter(monitor) # DeprecationWarning triggered on first invocation of generator action, device = pytest.deprecated_call(next, iterator) assert action == 'add' assert device.action == 'add' assert device.sequence_number > 0 assert device.subsystem == 'net' assert device.device_path == '/devices/virtual/net/dummy0' pytest.unload_dummy() action, device = next(iterator) assert action == 'remove' assert device.action == 'remove' assert device.sequence_number > 0 assert device.subsystem == 'net' assert device.device_path == '/devices/virtual/net/dummy0' iterator.close() 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: 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 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() # check that we got two events assert self.events == [fake_monitor_device] * 2 @pytest.mark.privileged def test_real(self, context, monitor): observer = self.make_observer(monitor) pytest.unload_dummy() monitor.filter_by('net') monitor.start() observer.start() pytest.load_dummy() pytest.unload_dummy() observer.join(2) if observer.is_alive(): observer.stop() assert [d.action for d in self.events] == ['add', 'remove'] for device in self.events: assert device.device_path == '/devices/virtual/net/dummy0' pyudev-0.16.1/tests/test_observer.py0000644000175000001440000002302712006301274020532 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest from mock import Mock from pyudev import Monitor, Device from pyudev._libudev import libudev def pytest_funcarg__monitor(request): return Monitor.from_netlink(request.getfuncargvalue('context')) def pytest_funcarg__fake_monitor_device(request): context = request.getfuncargvalue('context') return Device.from_path(context, '/devices/platform') 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 ACTIONS = ('add', 'remove', 'change', 'move') 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 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(side_effect=self.stop_when_done) action_callback = Mock(side_effect=self.stop_when_done) self.connect_signal(event_callback) self.connect_signal(action_callback, action=action) calls = {'udev_device_get_action': [(fake_monitor_device,), (fake_monitor_device,)]} with pytest.calls_to_libudev(calls): libudev.udev_device_get_action.return_value = action.encode('ascii') self.start_event_loop(fake_monitor.trigger_event) event_callback.assert_called_with(action, fake_monitor_device) action_callback.assert_called_with(fake_monitor_device) @pytest.mark.privileged def test_events_real(self, context, monitor): # make sure that the module is unloaded initially pytest.unload_dummy() monitor.filter_by('net') monitor.start() self.prepare_test(monitor) # setup signal handlers event_callback = Mock(side_effect=self.stop_when_done) added_callback = Mock(side_effect=self.stop_when_done) removed_callback = Mock(side_effect=self.stop_when_done) self.connect_signal(event_callback) self.connect_signal(added_callback, action='add') self.connect_signal(removed_callback, action='remove') # test add event self.start_event_loop(pytest.load_dummy) device = Device.from_path(context, '/devices/virtual/net/dummy0') event_callback.assert_called_with('add', device) added_callback.assert_called_with(device) assert not removed_callback.called for mock in (event_callback, added_callback, removed_callback): mock.reset_mock() self.start_event_loop(pytest.unload_dummy) event_callback.assert_called_with('remove', device) assert not added_callback.called removed_callback.assert_called_with(device) class QtObserverTestBase(ObserverTestBase): 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 TestPysideObserver(QtObserverTestBase): BINDING_NAME = 'PySide' class TestPyQt4Observer(QtObserverTestBase): BINDING_NAME = 'PyQt4' class TestGlibObserver(ObserverTestBase): ACTION_SIGNAL_MAP = { 'add': 'device-added', 'remove': 'device-removed', 'change': 'device-changed', 'move': 'device-moved', } def setup(self): self.event_sources = [] self.glib = pytest.importorskip('glib') # make sure that we also have gobject pytest.importorskip('gobject') def teardown(self): for source in self.event_sources: self.glib.source_remove(source) def create_observer(self, monitor): 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 TestWxObserver(ObserverTestBase): def setup(self): self.wx = pytest.importorskip('wx') def create_observer(self, monitor): 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: 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() pyudev-0.16.1/tests/test_enumerate.py0000644000175000001440000002371612006301274020675 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import pytest import mock from pyudev import Enumerator, Device def pytest_funcarg__enumerator(request): context = request.getfuncargvalue('context') return context.list_devices() class TestEnumerator(object): def test_match_subsystem(self, context): devices = context.list_devices().match_subsystem('input') for device in devices: assert device.subsystem == 'input' def test_match_subsystem_nomatch(self, context): devices = context.list_devices().match_subsystem('input', nomatch=True) for device in devices: assert device.subsystem != 'input' def test_match_subsystem_nomatch_unfulfillable(self, context): devices = context.list_devices() devices.match_subsystem('input') devices.match_subsystem('input', nomatch=True) assert not list(devices) def test_match_sys_name(self, context): devices = context.list_devices().match_sys_name('sda') for device in devices: assert device.sys_name == 'sda' def test_match_property_string(self, context): devices = list(context.list_devices().match_property('DRIVER', 'usb')) for device in devices: assert device['DRIVER'] == 'usb' assert device.driver == 'usb' def test_match_property_int(self, context): devices = list(context.list_devices().match_property( 'ID_INPUT_KEY', 1)) for device in devices: assert device['ID_INPUT_KEY'] == '1' assert device.asint('ID_INPUT_KEY') == 1 def test_match_property_bool(self, context): devices = list(context.list_devices().match_property( 'ID_INPUT_KEY', True)) for device in devices: assert device['ID_INPUT_KEY'] == '1' assert device.asbool('ID_INPUT_KEY') def test_match_attribute_nomatch(self, context): devices = context.list_devices().match_attribute( 'driver', 'usb', nomatch=True) for device in devices: assert device.attributes.get('driver') != 'usb' def test_match_attribute_nomatch_unfulfillable(self, context): devices = context.list_devices() devices.match_attribute('driver', 'usb') devices.match_attribute('driver', 'usb', nomatch=True) assert not list(devices) def test_match_attribute_string(self, context): devices = list(context.list_devices().match_attribute('driver', 'usb')) for device in devices: assert device.attributes['driver'] == b'usb' def test_match_attribute_int(self, context): # busnum gives us the number of a USB bus. And any decent system # likely has two or more usb buses, so this should work on more or less # any system. I didn't find any other attribute that is likely to be # present on a wide range of system, so this is probably as general as # possible. Still it may fail because the attribute isn't present on # any device at all on the system running the test devices = list(context.list_devices().match_attribute('busnum', 2)) for device in devices: assert device.attributes['busnum'] == b'2' assert device.attributes.asint('busnum') == 2 def test_match_attribute_bool(self, context): # ro tells us whether a volumne is mounted read-only or not. And any # developers system should have at least one readable volume, thus this # test should work on all systems these tests are ever run on devices = list(context.list_devices().match_attribute('ro', False)) for device in devices: assert device.attributes['ro'] == b'0' assert not device.attributes.asbool('ro') @pytest.mark.udev_version('>= 154') def test_match_tag_mock(self, context): enumerator = context.list_devices() calls = {'udev_enumerate_add_match_tag': [(enumerator, b'spam')]} with pytest.calls_to_libudev(calls): retval = enumerator.match_tag('spam') assert retval is enumerator @pytest.mark.udev_version('>= 154') def test_match_tag(self, context): devices = list(context.list_devices().match_tag('seat')) for device in devices: assert 'seat' in device.tags @pytest.mark.parametrize('device_data', pytest.config.udev_device_sample) @pytest.mark.udev_version('>= 172') def test_match_parent(self, context, device_data): device = Device.from_path(context, device_data.device_path) parent = device.parent if parent is None: pytest.skip('Device {0!r} has no parent'.format(device)) else: children = list(context.list_devices().match_parent(parent)) assert device in children assert parent in children @pytest.mark.udev_version('>= 165') def test_match_is_initialized_mock(self, context): enumerator = context.list_devices() calls = {'udev_enumerate_add_match_is_initialized': [(enumerator,)]} with pytest.calls_to_libudev(calls): enumerator.match_is_initialized() def test_combined_matches_of_same_type(self, context): """ Test for behaviour as observed in #1 """ properties = ('DEVTYPE', 'ID_TYPE') devices = context.list_devices() for property in properties: devices.match_property(property, 'disk') for device in devices: assert (device.get('DEVTYPE') == 'disk' or device.get('ID_TYPE') == 'disk') def test_combined_matches_of_different_types(self, context): properties = ('DEVTYPE', 'ID_TYPE') devices = context.list_devices().match_subsystem('input') for property in properties: devices.match_property(property, 'disk') devices = list(devices) assert not devices def test_match(self, context): devices = list(context.list_devices().match( subsystem='input', ID_INPUT_MOUSE=True, sys_name='mouse0')) for device in devices: assert device.subsystem == 'input' assert device.asbool('ID_INPUT_MOUSE') assert device.sys_name == 'mouse0' def test_match_passthrough_subsystem(self, enumerator): 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) def test_match_passthrough_sys_name(self, enumerator): 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) def test_match_passthrough_tag(self, enumerator): 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) @pytest.mark.udev_version('>= 172') def test_match_passthrough_parent(self, enumerator): 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) def test_match_passthrough_property(self, enumerator): 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 class TestContext(object): @pytest.mark.match def test_list_devices(self, context): devices = list(context.list_devices( subsystem='input', ID_INPUT_MOUSE=True, sys_name='mouse0')) for device in devices: assert device.subsystem == 'input' assert device.asbool('ID_INPUT_MOUSE') assert device.sys_name == 'mouse0' @pytest.mark.match def test_list_devices_passthrough(self, context): with mock.patch.object(Enumerator, 'match') as match: context.list_devices(subsystem=mock.sentinel.subsystem, sys_name=mock.sentinel.sys_name, tag=mock.sentinel.tag, parent=mock.sentinel.parent, prop1=mock.sentinel.prop1, prop2=mock.sentinel.prop2) match.assert_called_with( subsystem=mock.sentinel.subsystem, sys_name=mock.sentinel.sys_name, tag=mock.sentinel.tag, parent=mock.sentinel.parent, prop1=mock.sentinel.prop1, prop2=mock.sentinel.prop2) pyudev-0.16.1/tests/plugins/0000755000175000001440000000000012006442367016760 5ustar swiesnerusers00000000000000pyudev-0.16.1/tests/plugins/libudev.py0000644000175000001440000001660212006301274020760 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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.libudev =============== Provide a list of all libudev functions as declared in ``libudev.h``. This plugin parses ``libudev.h`` with :program:`gccxml` and extracts all libudev functions from this header. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) from collections import namedtuple from operator import attrgetter from tempfile import NamedTemporaryFile from subprocess import check_call from xml.etree import cElementTree as etree import py.path import pytest class GCCXMLParser(object): """ The parser to parse translation units. """ @classmethod def default_parser(cls): """ Create a default parser. This method searches :program:`gccxml` in ``$PATH``. Return a :class:`GCCXMLParser`, if :program:`gccxml` is found. Raise :exc:`~exceptions.LookupError` otherwise. """ gccxml = py.path.local.sysfind('gccxml') if not gccxml: raise LookupError('Could not find "gccxml"') return cls(str(gccxml)) def __init__(self, gccxml): self.gccxml = gccxml def parse(self, filename): # by opening the file in Python and piping it to gccxml we get nice # Python exceptions if filename can't be read, instead of mucking # around with the gccxml return value. with open(filename, 'r') as source: with NamedTemporaryFile() as sink: cmd = [self.gccxml, '-', '-fxml={0}'.format(sink.name)] check_call(cmd, stdin=source) return etree.parse(sink.name) # wrap symbol types required to represent libudev declarations into nice tuples Function = namedtuple('Function', 'name arguments return_type') # libudev only uses forward-declared structs, so we can ignore struct members Struct = namedtuple('Struct', 'name') FundamentalType = namedtuple('FundamentalType', 'name') CvQualifiedType = namedtuple('CvQualifiedType', 'type') PointerType = namedtuple('PointerType', 'type') Typedef = namedtuple('Typedef', 'type') class Unit(object): """ A translation unit. Parses a translation unit and provides a list of all symbols in this unit. """ @classmethod def parse(cls, filename, parser=None): """ Parse the translation unit denoted by ``filename``. ``filename`` is a string denoting the file to parse. ``parser`` is a :class:`GCCXMLParser` to use for parsing. If ``None``, :meth:`GCCXMLParser.default_parser()` is used. Return a :class:`Unit` representing the parsed unit. Raise :exc:`~exceptions.EnvironmentError`, if ``filename`` could not be opened or read. """ if parser is None: parser = GCCXMLParser.default_parser() tree = parser.parse(filename) return cls.from_tree(tree) @classmethod def from_tree(cls, tree): return cls(tree) def __init__(self, tree): self.tree = tree self._symbol_table = {} self._symbols = {} def _build_symbol_table(self): if self._symbol_table: return for symbol in self.tree.getroot(): self._symbol_table[symbol.get('id')] = symbol def _resolve_Function(self, symbol): return_type = self._resolve_symbol(symbol.get('returns')) arguments = tuple(self._resolve_symbol(a.get('type')) for a in symbol.findall('./Argument')) return Function(symbol.get('name'), arguments, return_type) def _resolve_Struct(self, symbol): return Struct(symbol.get('name')) def _resolve_FundamentalType(self, symbol): return FundamentalType(symbol.get('name')) def _resolve_CvQualifiedType(self, symbol): return CvQualifiedType(self._resolve_symbol(symbol.get('type'))) def _resolve_PointerType(self, symbol): return PointerType(self._resolve_symbol(symbol.get('type'))) def _resolve_Typedef(self, symbol): return Typedef(self._resolve_symbol(symbol.get('type'))) def _resolve_symbol(self, symbol): if not isinstance(symbol, type(self.tree.getroot())): symbol = self._symbol_table[symbol] symbol_id = symbol.get('id') if symbol_id not in self._symbols: resolve_func_name = '_resolve_{0}'.format(symbol.tag) resolve = getattr(self, resolve_func_name, None) self._symbols[symbol_id] = resolve(symbol) if resolve else None return self._symbols[symbol_id] def _resolve_symbols(self): if self._symbols: return for symbol in self.tree.getroot(): self._resolve_symbol(symbol) @property def symbols(self): """ Yield all symbols in this unit. .. warning:: This does not really yield *all* symbols, but only those symbol types that are required to parse ``libudev.h``. Other symbols, e.g. C++ classes or namespaces, are ignored. """ self._build_symbol_table() self._resolve_symbols() return self._symbols.values() @property def functions(self): """ Yield all functions in this unit as :class:`Function` objects. """ return (s for s in self.symbols if isinstance(s, Function)) LIBUDEV_H = '/usr/include/libudev.h' def pytest_configure(config): try: libudev_h = Unit.parse(LIBUDEV_H) config.libudev_functions = [f for f in libudev_h.functions if f.name.startswith('udev_')] config.libudev_error = None except (EnvironmentError, LookupError) as error: config.libudev_functions = [] config.libudev_error = error def pytest_generate_tests(metafunc): if 'libudev_function' in metafunc.funcargnames: functions = sorted(metafunc.config.libudev_functions, key=attrgetter('name')) if functions: metafunc.parametrize('libudev_function', functions, indirect=True, ids=[f.name for f in functions]) else: metafunc.parametrize('libudev_function', [metafunc.config.libudev_error], indirect=True) def pytest_funcarg__libudev_function(request): """ Return each :class:`Function` parsed from libudev. If libudev could not be parsed, the invoking test is skipped. """ if isinstance(request.param, Function): return request.param else: pytest.skip(str(request.param)) pyudev-0.16.1/tests/plugins/privileged.py0000644000175000001440000000426012006301274021455 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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.privileged ================== Support privileged operations to trigger real udev events. This plugin adds :func:`load_dummy` and :func:`unload_dummy` to the :mod:`pytest` namespace. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) from subprocess import call import pytest def pytest_addoption(parser): group = parser.getgroup('privileged', 'tests with privileged operations') group.addoption('--enable-privileged', action='store_true', help='Enable tests that required privileged operations', default=False) def check_privileges_or_skip(): if not pytest.config.option.enable_privileged: pytest.skip('privileged tests disabled') def load_dummy(): """ Load the ``dummy`` module. If privileged tests are disabled, the current test is skipped. """ check_privileges_or_skip() call(['sudo', 'modprobe', 'dummy']) def unload_dummy(): """ Unload the ``dummy`` module. If privileged tests are disabled, the current test is skipped. """ check_privileges_or_skip() call(['sudo', 'modprobe', '-r', 'dummy']) EXPOSED_FUNCTIONS = [load_dummy, unload_dummy] def pytest_namespace(): return dict((f.__name__, f) for f in EXPOSED_FUNCTIONS) pyudev-0.16.1/tests/plugins/fake_monitor.py0000644000175000001440000000565112006301274022005 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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 as provided by the :mod:`~plugins.privileged` plugin. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import sys import os from select import select 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) def pytest_funcarg__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.getfuncargvalue('fake_monitor_device')) pyudev-0.16.1/tests/plugins/__init__.py0000644000175000001440000000207712006301274021066 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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 """ from __future__ import (print_function, division, unicode_literals, absolute_import) pyudev-0.16.1/tests/plugins/udev_database.py0000644000175000001440000002424312006301274022115 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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.udev_database ===================== Provide access the udev device database. This plugin parses the udev device database from :program:`udevadm` and attaches it to the test configuration object. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import sys import os import re import errno import random import subprocess from collections import Iterable, Sized import pytest class UDevAdm(object): """ Wrap ``udevadm`` utility. """ CANDIDATES = ['/sbin/udevadm', 'udevadm'] @classmethod def find(cls): 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 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 int(self._execute('--version')) def _execute(self, *args): 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'): output = self._execute('info', '--root', '--path', device_path, '--query', query_type) return output.decode(sys.getfilesystemencoding()) def query_devices(self): database = self._execute('info', '--export-db').decode( sys.getfilesystemencoding()).splitlines() for line in database: line = line.strip() if not line: continue type, value = line.split(': ', 1) if type == 'P': yield value def query_device_properties(self, device_path): properties = {} for line in self._execute_query(device_path, 'property').splitlines(): line = line.strip() property, value = line.split('=', 1) properties[property] = value return properties 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 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. """ if self.device_node: return os.stat(self.device_node).st_rdev else: return 0 class DeviceDatabase(Iterable, Sized): """ The udev device database. This class is an iterable over :class:`DeviceData` objects that contain the data associated with each device stored in the udev database. """ def __init__(self, udevadm): self._udevadm = udevadm self._devices = set(self._udevadm.query_devices()) def __iter__(self): return (DeviceData(d, self._udevadm) for d in self._devices) def __len__(self): return len(self._devices) 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. """ if device_path in self._devices: return DeviceData(device_path, self._udevadm) else: return None def _get_device_sample(config): """ Compute a sample of the udev device database based on the given pytest ``config``. """ if config.option.device is not None: device_data = config.udev_database.find_device_data( config.option.device) if not device_data: raise ValueError('{0} does not exist'.format( config.option.device)) return [device_data] elif config.option.all_devices: return list(config.udev_database) else: device_sample_size = config.getvalue('device_sample_size') actual_size = min(device_sample_size, len(config.udev_database)) return random.sample(list(config.udev_database), actual_size) def pytest_runtest_setup(item): """ Evaluate the ``udev_version`` marker before running a test. """ if not hasattr(item, 'obj'): return marker = getattr(item.obj, 'udev_version', None) if marker is not None: version_spec = marker.args[0] actual_version = item.config.udev_version if not eval('{0} {1}'.format(actual_version, version_spec)): msg = 'udev version mismatch: {0} required, {1} found'.format( version_spec, actual_version) pytest.skip(msg) def pytest_addoption(parser): group = parser.getgroup('udev_database', 'udev database configuration') group.addoption('--all-devices', action='store_true', help='Run device tests against *all* devices in the ' 'database. By default, only a random sample will be ' 'checked.', default=False) group.addoption('--device', metavar='DEVICE', help='Run the device tests only against the given ' 'DEVICE', default=None) group.addoption('--device-sample-size', type='int', metavar='N', help='Use a random sample of N elements (default: 10)', default=10) def pytest_configure(config): # register a marker for required udev versions config.addinivalue_line( 'markers', 'udev_version(spec): mark test to run only if the udev ' 'version matches the given version spec') udevadm = UDevAdm.find() config.udev_version = udevadm.query_udev_version() config.udev_database = DeviceDatabase(udevadm) config.udev_device_sample = _get_device_sample(config) def pytest_funcarg__udev_database(request): """ The udev database as provided by :attr:`pytest.config.udev_database`. """ return request.config.udev_database pyudev-0.16.1/tests/plugins/mock_libudev.py0000644000175000001440000001047512006301274021773 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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:`calls_to_libudev()` and :func:`libudev_list()` to the :mod:`pytest` namespace. .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) from operator import attrgetter from contextlib import contextmanager from collections import namedtuple import mock @contextmanager def calls_to_libudev(function_calls): """ Mock libudev functions and check calls to the mocked functions:: calls = {'udev_device_ref': [(device,)]} with pytest.calls_to_libudev(calls): device.parent ``function_calls`` is a dictionary that maps libudev function names to a list of calls, where each call is represented as tuple containing the arguments expected to be passed to the function. If any call in ``function_calls`` does not occur, the function triggers an assertion. All mocked functions are restored if the context exits. """ from pyudev._libudev import libudev mocks = dict((function, mock.DEFAULT) for function in function_calls) with mock.patch.multiple(libudev, **mocks): yield for name, calls in function_calls.items(): function = getattr(libudev, name) function.assert_has_calls([mock.call(*c) for c in calls]) 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(function, items): """ Mock a libudev linked list:: with pytest.libudev_list('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. """ from pyudev._libudev import libudev 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 = [calls_to_libudev, libudev_list] def pytest_namespace(): return dict((f.__name__, f) for f in EXPOSED_FUNCTIONS) pyudev-0.16.1/tests/test_pypi.py0000644000175000001440000000461112006442153017664 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import re import os import subprocess from distutils.filelist import FileList 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$'] 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] 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') 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 pyudev-0.16.1/tests/test_device.py0000644000175000001440000005562312006301274020151 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 from __future__ import (print_function, division, unicode_literals, absolute_import) import re import os import stat import operator import sys import gc import errno from itertools import count from datetime import timedelta import pytest from mock import sentinel from pyudev import (Device, DeviceNotFoundAtPathError, DeviceNotFoundByNameError, DeviceNotFoundByNumberError, DeviceNotFoundInEnvironmentError) from pyudev.device import Attributes, Tags from pyudev._libudev import libudev with_device_data = pytest.mark.parametrize( 'device_data', pytest.config.udev_device_sample, ids=pytest.config.udev_device_sample) with_devices = pytest.mark.parametrize( 'device', pytest.config.udev_device_sample, indirect=True, ids=pytest.config.udev_device_sample) def pytest_funcarg__device(request): device_data = getattr(request, 'param', None) or \ request.getfuncargvalue('device_data') context = request.getfuncargvalue('context') return Device.from_path(context, device_data.device_path) class TestDevice(object): @with_device_data def test_from_path(self, context, device_data): device = Device.from_path(context, device_data.device_path) assert device is not None assert device == Device.from_sys_path(context, device_data.sys_path) assert device == Device.from_path(context, device_data.sys_path) def test_from_path_strips_leading_slash(self, context): assert Device.from_path(context, 'devices/platform') == \ Device.from_path(context, '/devices/platform') @with_device_data def test_from_sys_path(self, context, device_data): device = Device.from_sys_path(context, device_data.sys_path) assert device is not None assert device.sys_path == device_data.sys_path def test_from_sys_path_device_not_found(self, context): sys_path = 'there_will_not_be_such_a_device' with pytest.raises(DeviceNotFoundAtPathError) as exc_info: Device.from_sys_path(context, sys_path) error = exc_info.value assert error.sys_path == sys_path assert str(error) == 'No device at {0!r}'.format(sys_path) @with_devices def test_from_name(self, context, device): new_device = Device.from_name(context, device.subsystem, device.sys_name) assert new_device == device def test_from_name_no_device_in_existing_subsystem(self, context): with pytest.raises(DeviceNotFoundByNameError) as exc_info: Device.from_name(context, 'block', 'foobar') error = exc_info.value assert error.subsystem == 'block' assert error.sys_name == 'foobar' assert str(error) == 'No device {0!r} in {1!r}'.format( error.sys_name, error.subsystem) def test_from_name_nonexisting_subsystem(self, context): with pytest.raises(DeviceNotFoundByNameError) as exc_info: Device.from_name(context, 'no_such_subsystem', 'foobar') error = exc_info.value assert error.subsystem == 'no_such_subsystem' assert error.sys_name == 'foobar' assert str(error) == 'No device {0!r} in {1!r}'.format( error.sys_name, error.subsystem) @with_device_data def test_from_device_number(self, context, device_data): if not device_data.device_node: pytest.skip('no device node, no device number') mode = os.stat(device_data.device_node).st_mode type = 'block' if stat.S_ISBLK(mode) else 'char' device = Device.from_device_number( context, type, device_data.device_number) assert device.device_number == device_data.device_number # make sure, we are really referring to the same device assert device.device_path == device_data.device_path @with_device_data def test_from_device_number_wrong_type(self, context, device_data): if not device_data.device_node: pytest.skip('no device node, no device number') mode = os.stat(device_data.device_node).st_mode # deliberately use the wrong type here to cause either failure or at # least device mismatch type = '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 = Device.from_device_number( context, type, device_data.device_number) # if it succeeds, the resulting device must not match the one, we # are actually looking for! assert device.device_path != device_data.device_path except DeviceNotFoundByNumberError as error: # check the correctness of the exception attributes assert error.device_type == type assert error.device_number == device_data.device_number def test_from_device_number_invalid_type(self, context): with pytest.raises(ValueError) as exc_info: Device.from_device_number(context, 'foobar', 100) assert str(exc_info.value) == ('Invalid type: {0!r}. Must be one of ' '"char" or "block".'.format('foobar')) @with_device_data def test_from_device_file(self, context, device_data): if not device_data.device_node: pytest.skip('no device file') device = Device.from_device_file(context, device_data.device_node) assert device.device_node == device_data.device_node assert device.device_path == device_data.device_path @with_device_data def test_from_device_file_links(self, context, device_data): if not device_data.device_links: pytest.skip('no device links') for link in device_data.device_links: link = os.path.join(context.device_path, link) device = Device.from_device_file(context, link) assert device.device_path == device_data.device_path assert link in device.device_links def test_from_device_file_no_device_file(self, context, tmpdir): filename = tmpdir.join('test') filename.ensure(file=True) with pytest.raises(ValueError) as excinfo: Device.from_device_file(context, str(filename)) message = 'not a device file: {0!r}'.format(str(filename)) assert str(excinfo.value) == message def test_from_device_file_non_existing(self, context, tmpdir): filename = tmpdir.join('test') assert not tmpdir.check(file=True) with pytest.raises(EnvironmentError) as excinfo: Device.from_device_file(context, str(filename)) pytest.assert_env_error(excinfo.value, errno.ENOENT, str(filename)) @pytest.mark.udev_version('>= 152') def test_from_environment(self, context): # there is no device in a standard environment with pytest.raises(DeviceNotFoundInEnvironmentError): Device.from_environment(context) @with_devices def test_parent(self, device): assert device.parent is None or isinstance(device.parent, Device) @pytest.mark.udev_version('>= 172') @with_devices def test_child_of_parent(self, device): if device.parent is None: pytest.skip('Device {0!r} has no parent'.format(device)) else: assert device in device.parent.children @pytest.mark.udev_version('>= 172') @with_devices def test_children(self, device): children = list(device.children) if not children: pytest.skip('Device {0!r} has no children'.format(device)) else: for child in children: assert child != device assert device in child.ancestors @with_devices def test_ancestors(self, device): child = device for ancestor in device.ancestors: assert ancestor == child.parent child = ancestor @with_devices def test_find_parent(self, device): parent = device.find_parent(device.subsystem) if not parent: pytest.skip('no parent within the same subsystem') assert parent.subsystem == device.subsystem assert parent in device.ancestors @with_devices def test_find_parent_no_devtype_mock(self, device): calls = {'udev_device_get_parent_with_subsystem_devtype': [(device, b'subsystem', None)], 'udev_device_ref': [(sentinel.parent_device,)]} with pytest.calls_to_libudev(calls): f = libudev.udev_device_get_parent_with_subsystem_devtype f.return_value = sentinel.parent_device libudev.udev_device_ref.return_value = sentinel.ref_device parent = device.find_parent('subsystem') assert isinstance(parent, Device) assert parent._as_parameter_ is sentinel.ref_device @with_devices def test_find_parent_with_devtype_mock(self, device): calls = {'udev_device_get_parent_with_subsystem_devtype': [(device, b'subsystem', b'devtype')], 'udev_device_ref': [(sentinel.parent_device,)]} with pytest.calls_to_libudev(calls): f = libudev.udev_device_get_parent_with_subsystem_devtype f.return_value = sentinel.parent_device libudev.udev_device_ref.return_value = sentinel.ref_device parent = device.find_parent('subsystem', 'devtype') assert isinstance(parent, Device) assert parent._as_parameter_ is sentinel.ref_device @with_devices def test_traverse(self, device): child = device for parent in pytest.deprecated_call(device.traverse): assert parent == child.parent child = parent @with_device_data def test_sys_path(self, device, device_data): assert device.sys_path == device_data.sys_path assert pytest.is_unicode_string(device.sys_path) @with_device_data def test_device_path(self, device, device_data): assert device.device_path == device_data.device_path assert pytest.is_unicode_string(device.device_path) @with_device_data def test_subsystem(self, device, device_data): assert device.subsystem == device_data.properties['SUBSYSTEM'] assert pytest.is_unicode_string(device.subsystem) @with_devices def test_device_sys_name(self, device): assert device.sys_name == os.path.basename(device.device_path) assert pytest.is_unicode_string(device.sys_name) @with_devices def test_sys_number(self, device): match = re.search(r'\d+$', 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 device.sys_number == match.group(0) assert pytest.is_unicode_string(device.sys_name) else: assert device.sys_number is None @with_device_data def test_type(self, device, device_data): assert device.device_type == device_data.properties.get('DEVTYPE') if device.device_type: assert pytest.is_unicode_string(device.device_type) @with_device_data def test_driver(self, device, device_data): assert device.driver == device_data.properties.get('DRIVER') if device.driver: assert pytest.is_unicode_string(device.driver) @with_device_data def test_device_node(self, device, device_data): assert device.device_node == device_data.device_node if device.device_node: assert pytest.is_unicode_string(device.device_node) @with_device_data def test_device_number(self, device, device_data): assert device.device_number == device_data.device_number @pytest.mark.udev_version('>= 165') @with_devices def test_is_initialized(self, device): assert isinstance(device.is_initialized, bool) @pytest.mark.udev_version('>= 165') @with_devices def test_is_initialized_mock(self, device): calls = {'udev_device_get_is_initialized': [(device,)]} with pytest.calls_to_libudev(calls): libudev.udev_device_get_is_initialized.return_value = False assert not device.is_initialized @pytest.mark.udev_version('>= 165') @with_devices def test_time_since_initialized(self, device): assert isinstance(device.time_since_initialized, timedelta) @pytest.mark.udev_version('>= 165') @with_devices def test_time_since_initialized_mock(self, device): calls = {'udev_device_get_usec_since_initialized': [(device,)]} with pytest.calls_to_libudev(calls): libudev.udev_device_get_usec_since_initialized.return_value = 100 assert device.time_since_initialized.microseconds == 100 @with_device_data def test_links(self, context, device, device_data): assert sorted(device.device_links) == sorted(device_data.device_links) for link in device.device_links: assert pytest.is_unicode_string(link) @with_devices def test_action(self, device): assert device.action is None @with_devices def test_action_mock(self, device): calls = {'udev_device_get_action': [(device,)]} with pytest.calls_to_libudev(calls): libudev.udev_device_get_action.return_value = b'spam' assert device.action == 'spam' assert pytest.is_unicode_string(device.action) @with_devices @pytest.mark.seqnum def test_sequence_number(self, device): assert device.sequence_number == 0 @with_devices def test_attributes(self, device): # see TestAttributes for complete attribute tests assert isinstance(device.attributes, Attributes) def test_no_leak(self, context): """ 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 @with_devices def test_tags(self, device): # see TestTags for complete tag tests assert isinstance(device.tags, Tags) @with_device_data def test_iteration(self, device, device_data): for property in device: assert pytest.is_unicode_string(property) # test that iteration really yields all properties device_properties = set(device) for property in device_data.properties: assert property in device_properties @with_device_data def test_length(self, device, device_data): assert len(device) == len(device_data.properties) @with_device_data def test_getitem(self, device, device_data): for property in device_data.properties: assert device[property] == device_data.properties[property] @with_device_data def test_getitem_devname(self, context, device, device_data): if 'DEVNAME' not in device_data.properties: pytest.skip('%r has no DEVNAME' % device) data_devname = os.path.join( context.device_path, device_data.properties['DEVNAME']) device_devname = os.path.join(context.device_path, device['DEVNAME']) assert device_devname == data_devname @with_devices def test_getitem_nonexisting(self, device): with pytest.raises(KeyError) as excinfo: device['a non-existing property'] assert str(excinfo.value) == repr('a non-existing property') @with_device_data def test_asint(self, device, device_data): for property, value in device_data.properties.items(): try: value = int(value) except ValueError: with pytest.raises(ValueError): device.asint(property) else: assert device.asint(property) == value @with_device_data def test_asbool(self, device, device_data): for property, value in device_data.properties.items(): if value == '1': assert device.asbool(property) elif value == '0': assert not device.asbool(property) else: with pytest.raises(ValueError) as exc_info: device.asbool(property) message = 'Not a boolean value: {0!r}' assert str(exc_info.value) == message.format(value) @with_devices def test_hash(self, device): assert hash(device) == hash(device.device_path) assert hash(device.parent) == hash(device.parent) assert hash(device.parent) != hash(device) @with_devices def test_equality(self, device): assert device == device.device_path assert device == device assert device.parent == device.parent assert not (device == device.parent) @with_devices def test_inequality(self, device): assert not (device != device.device_path) assert not (device != device) assert not (device.parent != device.parent) assert device != device.parent ORDERING_OPERATORS = [operator.gt, operator.lt, operator.le, operator.ge] @pytest.mark.parametrize( 'operator', ORDERING_OPERATORS, ids=[f.__name__ for f in ORDERING_OPERATORS]) def test_device_ordering(self, context, operator): device = Device.from_path(context, '/devices/platform') with pytest.raises(TypeError) as exc_info: operator(device, device) assert str(exc_info.value) == 'Device not orderable' class TestAttributes(object): @with_devices def test_length(self, device): counter = count() for _ in device.attributes: next(counter) assert len(device.attributes) == next(counter) @with_device_data def test_iteration(self, device, device_data): for attribute in device.attributes: assert pytest.is_unicode_string(attribute) # check that iteration really yields all attributes device_attributes = set(device.attributes) for attribute in device_data.attributes: assert attribute in device_attributes @pytest.mark.udev_version('>= 167') @with_devices def test_iteration_mock(self, device): attributes = [b'spam', b'eggs'] get_sysattr_list = 'udev_device_get_sysattr_list_entry' with pytest.libudev_list(get_sysattr_list, attributes): attrs = list(device.attributes) assert attrs == ['spam', 'eggs'] f = libudev.udev_device_get_sysattr_list_entry f.assert_called_with(device) @with_device_data def test_contains(self, device, device_data): for attribute in device_data.attributes: assert attribute in device.attributes @with_device_data def test_getitem(self, device, device_data): for attribute, value in device_data.attributes.items(): raw_value = value.encode(sys.getfilesystemencoding()) assert isinstance(device.attributes[attribute], bytes) assert device.attributes[attribute] == raw_value @with_devices def test_getitem_nonexisting(self, device): with pytest.raises(KeyError) as excinfo: device.attributes['a non-existing attribute'] assert str(excinfo.value) == repr('a non-existing attribute') @with_device_data def test_asstring(self, device, device_data): for attribute, value in device_data.attributes.items(): assert pytest.is_unicode_string( device.attributes.asstring(attribute)) assert device.attributes.asstring(attribute) == value @with_device_data def test_asint(self, device, device_data): for attribute, value in device_data.attributes.items(): try: value = int(value) except ValueError: with pytest.raises(ValueError): device.attributes.asint(attribute) else: assert device.attributes.asint(attribute) == value @with_device_data def test_asbool(self, device, device_data): for attribute, value in device_data.attributes.items(): if value == '1': assert device.attributes.asbool(attribute) elif value == '0': assert not device.attributes.asbool(attribute) else: with pytest.raises(ValueError) as exc_info: device.attributes.asbool(attribute) message = 'Not a boolean value: {0!r}' assert str(exc_info.value) == message.format(value) class TestTags(object): pytestmark = pytest.mark.udev_version('>= 154') @with_device_data def test_iteration(self, device, device_data): if not device_data.tags: pytest.skip('no tags on device') assert set(device.tags) == set(device_data.tags) for tag in device.tags: assert pytest.is_unicode_string(tag) @with_devices def test_iteration_mock(self, device): with pytest.libudev_list('udev_device_get_tags_list_entry', [b'spam', b'eggs']): tags = list(device.tags) assert tags == ['spam', 'eggs'] f = libudev.udev_device_get_tags_list_entry f.assert_called_with(device) @with_device_data def test_contains(self, device, device_data): if not device_data.tags: pytest.skip('no tags on device') for tag in device_data.tags: assert tag in device.tags @pytest.mark.udev_version('>= 172') @with_devices def test_contains_mock(self, device): """ Test that ``udev_device_has_tag`` is called if available. """ calls = {'udev_device_has_tag': [(device, b'foo')]} with pytest.calls_to_libudev(calls): libudev.udev_device_has_tag.return_value = 1 assert 'foo' in device.tags pyudev-0.16.1/setup.py0000644000175000001440000000404712006301274015643 0ustar swiesnerusers00000000000000#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2010, 2011, 2012 Sebastian Wiesner # 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 setuptools if sys.version_info[0] < 3: from codecs import open import pyudev with open('README.rst', encoding='utf-8') as stream: long_description = stream.read() setuptools.setup( name='pyudev', version=str(pyudev.__version__), url='http://pyudev.readthedocs.org/', author='Sebastian Wiesner', author_email='lunaryorn@gmail.com', description='A libudev binding', long_description=long_description, platforms=['Linux'], license='LGPL 2.1+', 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 :: 2', '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', ], packages=setuptools.find_packages(), ) pyudev-0.16.1/PKG-INFO0000644000175000001440000001232312006442367015233 0ustar swiesnerusers00000000000000Metadata-Version: 1.1 Name: pyudev Version: 0.16.1 Summary: A libudev binding Home-page: http://pyudev.readthedocs.org/ Author: Sebastian Wiesner Author-email: lunaryorn@gmail.com License: LGPL 2.1+ Description: ###### pyudev ###### .. image:: https://secure.travis-ci.org/lunaryorn/pyudev.png?branch=master :target: http://travis-ci.org/lunaryorn/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_ 2 (2.6 or newer) and 3 (3.1 or newer), and PyPy_ 1.5 or newer. 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 ask questions about pyudev on the mailing list at pyudev@librelist.com. To join this list, send a mail to pyudev@librelist.com and reply to the confirmation email. Please report issues 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/lunaryorn/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 `_. - Understand the `branching model `_. - Use a topic branch based on the ``develop`` branch to easily amend a pull request later, if necessary. - Write `good commit messages `_. - 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 `testsuite documentation `_). - 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. .. _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: http://www.kernel.org/pub/linux/utils/kernel/hotplug/libudev/ .. _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/lunaryorn/pyudev/issues .. _GitHub: http://github.com/lunaryorn/pyudev .. _git: http://www.git-scm.com/ .. _pep8: http://pypi.python.org/pypi/pep8/ Platform: Linux Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Hardware Classifier: Topic :: System :: Operating System Kernels :: Linux pyudev-0.16.1/build_bindings.py0000644000175000001440000002754112006301274017463 0ustar swiesnerusers00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2012 Sebastian Wiesner # 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 """ build_bindings ============== Script to build native bindings inside virtualenvs. This plugin builds pygobject, PyQt4.QtCore, PySide.QtCore and wxPython if these are not available. This feature is mainly intented for use with tox_, but can be used in normal virtualenvs, too. .. warning:: The directory pointed to by ``os.path.join(sys.prefix, 'lib')`` must be contained in the load path of shared libraries, otherwise bindings may fail to load. The tox_ configuration of pyudev implicitly sets ``LD_LIBRARY_PATH`` accordingly, but in your own virtual environments you need to do this yourself. A convenient way to accomplish this is virtualenvwrapper_. .. _tox: http://tox.testrun.org .. _virtualenvwrapper: http://www.doughellmann.com/projects/virtualenvwrapper/ .. moduleauthor:: Sebastian Wiesner """ from __future__ import (print_function, division, unicode_literals, absolute_import) import sys import platform import os import posixpath import errno from collections import defaultdict from subprocess import call, check_call try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse import py.path IS_CPYTHON = platform.python_implementation() == 'CPython' class MissingDependencyError(Exception): pass class MissingProgramError(KeyError): pass def ensure_directory(directory): try: os.makedirs(directory) except EnvironmentError as error: if error.errno != errno.EEXIST: raise class Programs(defaultdict): def __missing__(self, key): path = py.path.local.sysfind(key) if not path: raise MissingProgramError(key) return str(path) class Environment(object): def __init__(self, download_directory, build_directory): self.download_directory = download_directory self.build_directory = build_directory self.programs = Programs() # a basic build environment self.environ = dict(os.environ) self.environ['LD_LIBRARY_PATH'] = os.path.join( sys.prefix, 'lib') self.environ['PKG_CONFIG_PATH'] = os.path.join( sys.prefix, 'lib', 'pkgconfig') def prepare(self): ensure_directory(self.download_directory) ensure_directory(self.build_directory) def build(self, binding, force=False): binding(self, force=force).install() def build_all(self, bindings, force=False): for binding in bindings: self.build(binding, force=force) def have_module(modname): try: __import__(modname) return True except ImportError: return False class Binding(object): DEPENDS = [] def __init__(self, env, force=False): self.env = env self.build_directory = os.path.join(env.build_directory, self.NAME) self.force = force @property def is_installed(self): raise NotImplementedError() @property def can_build(self): return IS_CPYTHON @property def source_archive(self): filename = posixpath.basename(urlparse(self.SOURCE_URL).path) return os.path.join(self.env.download_directory, filename) @property def build_environment(self): return dict(self.env.environ) @property def number_of_builds(self): # build on all cores if possible try: from multiprocessing import cpu_count return cpu_count() except (NotImplementedError, ImportError): return 1 def have_pkg_config_package(self, package): command = [self.env.programs['pkg-config'], '--exists', package] return call(command, env=self.env.environ) == 0 def find_dependencies(self): return def check_call(self, command): check_call(command, cwd=self.build_directory, env=self.build_environment) def download(self): call([self.env.programs['wget'], '-c', '-O', self.source_archive, self.SOURCE_URL]) def extract(self): check_call([self.env.programs['tar'], 'xaf', self.source_archive, '-C', os.path.dirname(self.build_directory)]) def prepare(self): self.download() self.extract() self.find_dependencies() def install(self): if self.is_installed and not self.force: return self.env.build_all(self.DEPENDS) if not self.can_build: return self.prepare() self.build() def build(self): raise NotImplementedError() def make(self, target=None): command = [self.env.programs['make'], '-j{0}'.format(self.number_of_builds)] if target: command.append(target) self.check_call(command) def make_build_and_install(self): self.make() self.make('install') class AutotoolsBinding(Binding): CONFIGURE_EXTRA_ARGS = [] @property def build_environment(self): # tell autotools where to find the Python interpreter env = dict(self.env.environ) # autotools needs the flat name of the python interpreter to discover # the headers python = os.path.basename(sys.executable) # append version number if the interpreter executable doesn't have one, # to avoid ambiguities between virtualenv python and system python if not python[-1].isdigit(): python += '{0}.{1}'.format(*sys.version_info) env['PYTHON'] = python return env def autotools_configure(self, extra_args): command = ['./configure', '--prefix', sys.prefix] + extra_args self.check_call(command) def build(self): self.autotools_configure(self.CONFIGURE_EXTRA_ARGS) self.make_build_and_install() class PyGObject(AutotoolsBinding): NAME = 'pygobject-2.28.6' SOURCE_URL = ('http://ftp.gnome.org/pub/GNOME/sources/pygobject/2.28/' '{0}.tar.bz2'.format(NAME)) CONFIGURE_EXTRA_ARGS = ['--disable-introspection'] @property def is_installed(self): return have_module('glib') and have_module('gobject') RIVERBANK_DOWNLOADS = 'http://www.riverbankcomputing.com/static/Downloads' class Sip4(Binding): NAME = 'sip-4.13.3' SOURCE_URL = '{0}/sip4/{1}.tar.gz'.format(RIVERBANK_DOWNLOADS, NAME) @property def is_installed(self): return have_module('sip') def build(self): incdir = os.path.join(sys.prefix, 'include', 'sip') self.check_call([sys.executable, 'configure.py', '--incdir', incdir]) self.make_build_and_install() class PyQt4QtCore(Binding): NAME = 'PyQt-x11-gpl-4.9.4' SOURCE_URL = '{0}/PyQt4/{1}.tar.gz'.format(RIVERBANK_DOWNLOADS, NAME) DEPENDS = [Sip4] @property def is_installed(self): return have_module('PyQt4.QtCore') def find_dependencies(self): try: self.qmake = self.env.programs['qmake-qt4'] except KeyError: self.qmake = self.env.programs['qmake'] def build(self): command = [sys.executable, 'configure.py', '--confirm-license', '--no-designer-plugin', '--no-sip-files', '--no-qsci-api', '--qmake', self.qmake, '--enable', 'QtCore'] self.check_call(command) self.make_build_and_install() class CMakeBinding(Binding): CMAKE_EXTRA_ARGS = [] def cmake_configure(self, extra_args): # cmake uses out of source builds self.build_directory = os.path.join(self.build_directory, 'build') ensure_directory(self.build_directory) command = [self.env.programs['cmake'], '-DBUILD_TESTS=OFF', '-DCMAKE_BUILD_TYPE=RelWithDebInfo', '-DCMAKE_INSTALL_PREFIX={0}'.format(sys.prefix)] command += extra_args command += ['..'] self.check_call(command) def build(self): self.cmake_configure(self.CMAKE_EXTRA_ARGS) self.make_build_and_install() PYSIDE_DOWNLOADS = 'http://www.pyside.org/files' class Shiboken(CMakeBinding): NAME = 'shiboken-1.1.1' SOURCE_URL = '{0}/{1}.tar.bz2'.format(PYSIDE_DOWNLOADS, NAME) CMAKE_EXTRA_ARGS = [ '-DPython_ADDITIONAL_VERSIONS={0}.{1}'.format(*sys.version_info) ] if sys.version_info[0] == 3: CMAKE_EXTRA_ARGS += ['-DUSE_PYTHON3=ON'] @property def is_installed(self): return self.have_pkg_config_package('shiboken') DISABLED_QT_MODULES = [ 'QtGui', 'QtMultimedia', 'QtNetwork', 'QtOpenGL', 'QtScript', 'QtScriptTools', 'QtSql', 'QtSvg', 'QtWebKit', 'QtXml', 'QtXmlPatterns', 'QtDeclarative', 'phonon', 'QtUiTools', 'QtHelp', 'QtTest'] class PySideQtCore(CMakeBinding): NAME = 'pyside-qt4.7+1.1.1' SOURCE_URL = '{0}/{1}.tar.bz2'.format(PYSIDE_DOWNLOADS, NAME) DEPENDS = [Shiboken] CMAKE_EXTRA_ARGS = ['-DDISABLE_{0}=ON'.format(mod) for mod in DISABLED_QT_MODULES] @property def is_installed(self): return have_module('PySide.QtCore') class WxPython(Binding): NAME = 'wxPython-src-2.8.12.1' SOURCE_URL = ('http://downloads.sourceforge.net/wxpython/' '{0}.tar.bz2'.format(NAME)) @property def can_build(self): # wx doesn't support Python 3 return IS_CPYTHON and sys.version_info[0] == 2 @property def is_installed(self): return have_module('wx') def build(self): self.build_directory = os.path.join( self.build_directory, 'wxPython') cmd = [sys.executable, 'setup.py', 'WXPORT=gtk2', 'UNICODE=1', # need to invoke install commands individually, because # "install" invokes "install_headers" which insists on # installing headers to system directories despite of a # virtualenv prefix. Praise wx… 'build', 'install_lib', 'install_data'] self.check_call(cmd) AVAILABLE_BINDINGS = { 'pyqt4': PyQt4QtCore, 'pyside': PySideQtCore, 'pygobject': PyGObject, 'wxpython': WxPython, } def main(): from argparse import ArgumentParser parser = ArgumentParser() parser.add_argument('-d', '--download-directory', help='Download directory') parser.add_argument('-b', '--build-directory', help='Build directory') parser.add_argument('-f', '--force-build', action='store_true', help='Build even if the binding is already available') parser.add_argument('--binding', action='append', dest='bindings', help='Binding to build', choices=AVAILABLE_BINDINGS) parser.set_defaults(download_directory='/tmp/pyudev-build-bindings', build_directory='/tmp/pyudev-build-bindings') args = parser.parse_args() if not args.bindings: args.bindings = AVAILABLE_BINDINGS.keys() bindings = [AVAILABLE_BINDINGS[b] for b in args.bindings] env = Environment(args.download_directory, args.build_directory) env.prepare() env.build_all(bindings, force=args.force_build) if __name__ == '__main__': main() pyudev-0.16.1/CHANGES.rst0000644000175000001440000001622612006301274015735 0ustar swiesnerusers000000000000000.16.1 (Aug 08, 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`. pyudev-0.16.1/setup.cfg0000644000175000001440000000026512006442367015761 0ustar swiesnerusers00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [aliases] release = egg_info -RDb '' [upload_docs] upload_dir = build/sphinx/html [pytest] norecursedirs = .* _* build